moved kdeaccessibility kdeaddons kdeadmin kdeartwork kdebindings kdeedu kdegames...
[kdeedu.git] / kstars / kstars / tools / altvstime.cpp
blobf0028b84d26cd5ac44786e7b6550cd0419410095
1 /***************************************************************************
2 altvstime.cpp - description
3 -------------------
4 begin : wed nov 17 08:05:11 CET 2002
5 copyright : (C) 2002-2003 by Pablo de Vicente
6 email : vicente@oan.es
7 ***************************************************************************/
9 /***************************************************************************
10 * *
11 * This program is free software; you can redistribute it and/or modify *
12 * it under the terms of the GNU General Public License as published by *
13 * the Free Software Foundation; either version 2 of the License, or *
14 * (at your option) any later version. *
15 * *
16 ***************************************************************************/
18 #include <qlayout.h>
19 #include <qpainter.h>
20 #include <klocale.h>
21 #include <klineedit.h>
22 #include <klistbox.h>
23 #include <kpushbutton.h>
24 #include <kdialogbase.h>
26 #include "altvstime.h"
27 #include "altvstimeui.h"
28 #include "dms.h"
29 #include "dmsbox.h"
30 #include "kstars.h"
31 #include "kstarsdata.h"
32 #include "skypoint.h"
33 #include "skyobject.h"
34 #include "skyobjectname.h"
35 #include "ksnumbers.h"
36 #include "objectnamelist.h"
37 #include "simclock.h"
38 #include "finddialog.h"
39 #include "locationdialog.h"
41 #include "kstarsdatetime.h"
42 #include "libkdeedu/extdate/extdatetimeedit.h"
44 AltVsTime::AltVsTime( QWidget* parent) :
45 KDialogBase( KDialogBase::Plain, i18n( "Altitude vs. Time" ), Close, Close, parent )
47 ks = (KStars*) parent;
49 QFrame *page = plainPage();
51 setMainWidget(page);
52 topLayout = new QVBoxLayout( page, 0, spacingHint() );
54 View = new AVTPlotWidget( -12.0, 12.0, -90.0, 90.0, page );
55 View->setMinimumSize( 400, 400 );
56 View->setSizePolicy( QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding );
57 View->setXAxisType( KStarsPlotWidget::TIME );
58 View->setYAxisType( KStarsPlotWidget::ANGLE );
59 View->setShowGrid( false );
60 View->setXAxisLabel( i18n( "Local Time" ) );
61 View->setXAxisLabel2( i18n( "Local Sidereal Time" ) );
62 View->setYAxisLabel( i18n( "the angle of an object above (or below) the horizon", "Altitude" ) );
64 avtUI = new AltVsTimeUI( page );
65 avtUI->raBox->setDegType( false );
66 avtUI->decBox->setDegType( true );
68 //POST-3.2
69 //Doesn't make sense to manually adjust long/lat unless we can modify TZ also
70 avtUI->longBox->setReadOnly( true );
71 avtUI->latBox->setReadOnly( true );
73 topLayout->addWidget( View );
74 topLayout->addWidget( avtUI );
76 geo = ks->geo();
78 DayOffset = 0;
79 showCurrentDate();
80 if ( getDate().time().hour() > 12 ) DayOffset = 1;
82 avtUI->longBox->show( geo->lng() );
83 avtUI->latBox->show( geo->lat() );
85 computeSunRiseSetTimes();
87 setLSTLimits();
88 View->updateTickmarks();
90 connect( avtUI->browseButton, SIGNAL( clicked() ), this, SLOT( slotBrowseObject() ) );
91 connect( avtUI->cityButton, SIGNAL( clicked() ), this, SLOT( slotChooseCity() ) );
92 connect( avtUI->updateButton, SIGNAL( clicked() ), this, SLOT( slotUpdateDateLoc() ) );
93 connect( avtUI->clearButton, SIGNAL( clicked() ), this, SLOT( slotClear() ) );
94 connect( avtUI->addButton, SIGNAL( clicked() ), this, SLOT( slotAddSource() ) );
95 connect( avtUI->nameBox, SIGNAL( returnPressed() ), this, SLOT( slotAddSource() ) );
96 connect( avtUI->raBox, SIGNAL( returnPressed() ), this, SLOT( slotAddSource() ) );
97 connect( avtUI->decBox, SIGNAL( returnPressed() ), this, SLOT( slotAddSource() ) );
98 connect( avtUI->clearFieldsButton, SIGNAL( clicked() ), this, SLOT( slotClearBoxes() ) );
99 connect( avtUI->longBox, SIGNAL( returnPressed() ), this, SLOT( slotAdvanceFocus() ) );
100 connect( avtUI->latBox, SIGNAL( returnPressed() ), this, SLOT( slotAdvanceFocus() ) );
101 connect( avtUI->PlotList, SIGNAL( highlighted(int) ), this, SLOT( slotHighlight() ) );
103 pList.setAutoDelete(FALSE);
104 deleteList.setAutoDelete(TRUE); //needed for skypoints which may be created in this class
106 //ther edit boxes should not pass on the return key!
107 avtUI->nameBox->setTrapReturnKey( true );
108 avtUI->raBox->setTrapReturnKey( true );
109 avtUI->decBox->setTrapReturnKey( true );
111 setMouseTracking( true );
114 AltVsTime::~AltVsTime()
116 // no need to delete child widgets, Qt does it all for us
119 void AltVsTime::slotAddSource(void) {
120 bool objFound( false );
122 //First, attempt to find the object name in the list of known objects
123 if ( ! avtUI->nameBox->text().isEmpty() ) {
124 ObjectNameList &ObjNames = ks->data()->ObjNames;
125 QString text = avtUI->nameBox->text().lower();
127 for( SkyObjectName *name = ObjNames.first( text ); name; name = ObjNames.next() ) {
128 if ( name->text().lower() == text ) {
129 //object found
130 SkyObject *o = name->skyObject();
131 processObject( o );
133 objFound = true;
134 break;
138 if ( !objFound ) kdDebug() << "No object named " << avtUI->nameBox->text() << " found." << endl;
141 //Next, if the name, RA, and Dec fields are all filled, construct a new skyobject
142 //with these parameters
143 if ( !objFound && ! avtUI->nameBox->text().isEmpty() && ! avtUI->raBox->text().isEmpty() && ! avtUI->decBox->text().isEmpty() ) {
144 bool ok( true );
145 dms newRA( 0.0 ), newDec( 0.0 );
146 newRA = avtUI->raBox->createDms( false, &ok );
147 if ( ok ) newDec = avtUI->decBox->createDms( true, &ok );
149 //If the epochName is blank (or any non-double), we assume J2000
150 //Otherwise, precess to J2000.
151 KStarsDateTime dt;
152 dt.setFromEpoch( getEpoch( avtUI->epochName->text() ) );
153 long double jd = dt.djd();
154 if ( jd != J2000 ) {
155 SkyPoint ptest( newRA, newDec );
156 ptest.precessFromAnyEpoch( jd, J2000 );
157 newRA.setH( ptest.ra()->Hours() );
158 newDec.setD( ptest.dec()->Degrees() );
161 //make sure the coords do not already exist from another object
162 bool found(false);
163 for ( SkyPoint *p = pList.first(); p; p = pList.next() ) {
164 //within an arcsecond?
165 if ( fabs( newRA.Degrees() - p->ra()->Degrees() ) < 0.0003 && fabs( newDec.Degrees() - p->dec()->Degrees() ) < 0.0003 ) {
166 found = true;
167 break;
170 if ( found ) {
171 kdDebug() << "This point is already displayed; I will not duplicate it." << endl;
172 ok = false;
175 if ( ok ) {
176 SkyObject *obj = new SkyObject( 8, newRA, newDec, 1.0, avtUI->nameBox->text() );
177 deleteList.append( obj ); //this object will be deleted when window is destroyed
178 processObject( obj );
181 //If the Ra and Dec boxes are filled, but the name field is empty,
182 //move input focus to nameBox`
183 } else if ( avtUI->nameBox->text().isEmpty() && ! avtUI->raBox->text().isEmpty() && ! avtUI->decBox->text().isEmpty() ) {
184 avtUI->nameBox->QWidget::setFocus();
186 //nameBox is empty, and one of the ra or dec fields is empty. Move input focus to empty coord box
187 } else if ( avtUI->raBox->text().isEmpty() ) {
188 avtUI->raBox->QWidget::setFocus();
189 } else if ( avtUI->decBox->text().isEmpty() ) {
190 avtUI->decBox->QWidget::setFocus();
193 View->repaint(false);
196 //Use find dialog to choose an object
197 void AltVsTime::slotBrowseObject(void) {
198 FindDialog fd(ks);
199 if ( fd.exec() == QDialog::Accepted ) {
200 SkyObject *o = fd.currentItem()->objName()->skyObject();
201 processObject( o );
204 View->repaint();
207 void AltVsTime::processObject( SkyObject *o, bool forceAdd ) {
208 KSNumbers *num = new KSNumbers( getDate().djd() );
209 KSNumbers *oldNum = 0;
211 //If the object is in the solar system, recompute its position for the given epochLabel
212 if ( o && o->isSolarSystem() ) {
213 oldNum = new KSNumbers( ks->data()->ut().djd() );
214 o->updateCoords( num, true, geo->lat(), ks->LST() );
217 //precess coords to target epoch
218 o->updateCoords( num );
220 //If this point is not in list already, add it to list
221 bool found(false);
222 for ( SkyPoint *p = pList.first(); p; p = pList.next() ) {
223 if ( o->ra()->Degrees() == p->ra()->Degrees() && o->dec()->Degrees() == p->dec()->Degrees() ) {
224 found = true;
225 break;
228 if ( found && !forceAdd ) kdDebug() << "This point is already displayed; I will not duplicate it." << endl;
229 else {
230 pList.append( (SkyPoint*)o );
232 //make sure existing curves are thin and red
233 for ( int i=0; i < View->objectCount(); ++i ) {
234 KPlotObject *obj = View->object( i );
235 if ( obj->size() == 2 ) {
236 obj->setColor( "red" );
237 obj->setSize( 1 );
241 //add new curve with width=2, and color=white
242 KPlotObject *po = new KPlotObject( "", "white", KPlotObject::CURVE, 2, KPlotObject::SOLID );
243 for ( double h=-12.0; h<=12.0; h+=0.5 ) {
244 po->addPoint( new DPoint( h, findAltitude( o, h ) ) );
246 View->addObject( po );
248 avtUI->PlotList->insertItem( o->translatedName() );
249 avtUI->PlotList->setCurrentItem( avtUI->PlotList->count() - 1 );
250 avtUI->raBox->showInHours(o->ra() );
251 avtUI->decBox->showInDegrees(o->dec() );
252 avtUI->nameBox->setText(o->translatedName() );
254 //Set epochName to epoch shown in date tab
255 avtUI->epochName->setText( QString().setNum( getDate().epoch() ) );
257 kdDebug() << "Currently, there are " << View->objectCount() << " objects displayed." << endl;
259 //restore original position
260 if ( o->isSolarSystem() ) {
261 o->updateCoords( oldNum, true, ks->geo()->lat(), ks->LST() );
262 delete oldNum;
264 delete num;
267 double AltVsTime::findAltitude( SkyPoint *p, double hour ) {
268 hour += 24.0*(double)DayOffset;
270 //getDate converts the user-entered local time to UT
271 KStarsDateTime ut = getDate().addSecs( hour*3600.0 );
273 dms LST = geo->GSTtoLST( ut.gst() );
274 p->EquatorialToHorizontal( &LST, geo->lat() );
275 return p->alt()->Degrees();
278 void AltVsTime::slotHighlight(void) {
279 int iPlotList = avtUI->PlotList->currentItem();
281 //highlight the curve of the selected object
282 for ( int i=0; i<View->objectCount(); ++i ) {
283 KPlotObject *obj = View->object( i );
285 if ( i == iPlotList ) {
286 obj->setSize( 2 );
287 obj->setColor( "white" );
288 } else {
289 obj->setSize( 1 );
290 obj->setColor( "red" );
294 View->update();
296 int index = 0;
297 for ( SkyPoint *p = pList.first(); p; p = pList.next() ) {
298 if ( index == iPlotList ) {
299 avtUI->raBox->showInHours(p->ra() );
300 avtUI->decBox->showInDegrees(p->dec() );
301 avtUI->nameBox->setText(avtUI->PlotList->currentText() );
303 ++index;
307 //move input focus to the next logical widget
308 void AltVsTime::slotAdvanceFocus(void) {
309 if ( sender()->name() == QString( "nameBox" ) ) avtUI->addButton->setFocus();
310 if ( sender()->name() == QString( "raBox" ) ) avtUI->decBox->setFocus();
311 if ( sender()->name() == QString( "decbox" ) ) avtUI->addButton->setFocus();
312 if ( sender()->name() == QString( "longBox" ) ) avtUI->latBox->setFocus();
313 if ( sender()->name() == QString( "latBox" ) ) avtUI->updateButton->setFocus();
316 void AltVsTime::slotClear(void) {
317 if ( pList.count() ) pList.clear();
318 if ( deleteList.count() ) deleteList.clear();
319 avtUI->PlotList->clear();
320 avtUI->nameBox->clear();
321 avtUI->raBox->clear();
322 avtUI->decBox->clear();
323 avtUI->epochName->clear();
324 View->clearObjectList();
325 View->repaint();
328 void AltVsTime::slotClearBoxes(void) {
329 avtUI->nameBox->clear();
330 avtUI->raBox->clear() ;
331 avtUI->decBox->clear();
332 avtUI->epochName->setText( QString().setNum( getDate().epoch() ) );
335 void AltVsTime::computeSunRiseSetTimes() {
336 //Determine the time of sunset and sunrise for the desired date and location
337 //expressed as doubles, the fraction of a full day.
338 KStarsDateTime today = getDate();
340 SkyObject *oSun = (SkyObject*) ks->data()->PCat->planetSun();
341 double sunRise = -1.0 * oSun->riseSetTime( today.djd() + 1.0, geo, true ).secsTo(QTime()) / 86400.0;
342 double sunSet = -1.0 * oSun->riseSetTime( today.djd(), geo, false ).secsTo(QTime()) / 86400.0;
344 //check to see if Sun is circumpolar
345 //requires temporary repositioning of Sun to target date
346 KSNumbers *num = new KSNumbers( today.djd() );
347 KSNumbers *oldNum = new KSNumbers( ks->data()->ut().djd() );
348 dms LST = geo->GSTtoLST( getDate().gst() );
349 oSun->updateCoords( num, true, geo->lat(), &LST );
350 if ( oSun->checkCircumpolar( geo->lat() ) ) {
351 if ( oSun->alt()->Degrees() > 0.0 ) {
352 //Circumpolar, signal it this way:
353 sunRise = 0.0;
354 sunSet = 1.0;
355 } else {
356 //never rises, signal it this way:
357 sunRise = 0.0;
358 sunSet = -1.0;
362 //Notify the View about new sun rise/set times:
363 View->setSunRiseSetTimes( sunRise, sunSet );
365 //Restore Sun coordinates:
366 oSun->updateCoords( oldNum, true, ks->geo()->lat(), ks->LST() );
368 delete num;
369 delete oldNum;
372 void AltVsTime::slotUpdateDateLoc(void) {
373 KStarsDateTime today = getDate();
374 KSNumbers *num = new KSNumbers( today.djd() );
375 KSNumbers *oldNum = 0;
376 dms LST = geo->GSTtoLST( today.gst() );
378 //First determine time of sunset and sunrise
379 computeSunRiseSetTimes();
381 for ( unsigned int i = 0; i < avtUI->PlotList->count(); ++i ) {
382 QString oName = avtUI->PlotList->text( i ).lower();
383 ObjectNameList &ObjNames = ks->data()->ObjNames;
384 bool objFound(false);
386 for( SkyObjectName *name = ObjNames.first( oName ); name; name = ObjNames.next() ) {
387 if ( name->text().lower() == oName ) {
388 //object found
389 SkyObject *o = name->skyObject();
391 //If the object is in the solar system, recompute its position for the given date
392 if ( o->isSolarSystem() ) {
393 oldNum = new KSNumbers( ks->data()->ut().djd() );
394 o->updateCoords( num, true, geo->lat(), &LST );
397 //precess coords to target epoch
398 o->updateCoords( num );
400 //update pList entry
401 pList.replace( i, (SkyPoint*)o );
403 KPlotObject *po = new KPlotObject( "", "white", KPlotObject::CURVE, 1, KPlotObject::SOLID );
404 for ( double h=-12.0; h<=12.0; h+=0.5 ) {
405 po->addPoint( new DPoint( h, findAltitude( o, h ) ) );
407 View->replaceObject( i, po );
409 //restore original position
410 if ( o->isSolarSystem() ) {
411 o->updateCoords( oldNum, true, ks->data()->geo()->lat(), ks->LST() );
412 delete oldNum;
413 oldNum = 0;
416 objFound = true;
417 break;
421 if ( ! objFound ) { //assume unfound object is a custom object
422 pList.at(i)->updateCoords( num ); //precess to desired epoch
424 KPlotObject *po = new KPlotObject( "", "white", KPlotObject::CURVE, 1, KPlotObject::SOLID );
425 for ( double h=-12.0; h<=12.0; h+=0.5 ) {
426 po->addPoint( new DPoint( h, findAltitude( pList.at(i), h ) ) );
428 View->replaceObject( i, po );
432 if ( getDate().time().hour() > 12 ) DayOffset = 1;
433 else DayOffset = 0;
435 setLSTLimits();
436 slotHighlight();
437 View->repaint();
439 delete num;
442 void AltVsTime::slotChooseCity(void) {
443 LocationDialog ld(ks);
444 if ( ld.exec() == QDialog::Accepted ) {
445 GeoLocation *newGeo = ld.selectedCity();
446 if ( newGeo ) {
447 geo = newGeo;
448 avtUI->latBox->showInDegrees( geo->lat() );
449 avtUI->longBox->showInDegrees( geo->lng() );
454 int AltVsTime::currentPlotListItem() const {
455 return avtUI->PlotList->currentItem();
458 void AltVsTime::setLSTLimits(void) {
459 //UT at noon on target date
460 KStarsDateTime ut = getDate().addSecs( ((double)DayOffset + 0.5)*86400. );
462 dms lst = geo->GSTtoLST( ut.gst() );
463 View->setSecondaryLimits( lst.Hours(), lst.Hours() + 24.0, -90.0, 90.0 );
466 void AltVsTime::showCurrentDate (void)
468 KStarsDateTime dt = KStarsDateTime::currentDateTime();
469 if ( dt.time() > QTime( 12, 0, 0 ) ) dt = dt.addDays( 1 );
470 avtUI->dateBox->setDate( dt.date() );
473 KStarsDateTime AltVsTime::getDate (void)
475 //convert midnight local time to UT:
476 KStarsDateTime dt( avtUI->dateBox->date(), QTime() );
477 dt = geo->LTtoUT( dt );
478 return dt;
481 double AltVsTime::getEpoch (QString eName)
483 //If Epoch field not a double, assume J2000
484 bool ok(false);
485 double epoch = eName.toDouble(&ok);
487 if ( !ok ) {
488 kdDebug() << "Invalid Epoch. Assuming 2000.0." << endl;
489 return 2000.0;
492 return epoch;
495 AVTPlotWidget::AVTPlotWidget( double x1, double x2, double y1, double y2, QWidget *parent, const char* name )
496 : KStarsPlotWidget( x1, x2, y1, y2, parent, name )
498 //Default SunRise/SunSet values
499 SunRise = 0.25;
500 SunSet = 0.75;
503 void AVTPlotWidget::mousePressEvent( QMouseEvent *e ) {
504 mouseMoveEvent( e );
507 void AVTPlotWidget::mouseMoveEvent( QMouseEvent *e ) {
508 QRect checkRect( leftPadding(), topPadding(), PixRect.width(), PixRect.height() );
509 int Xcursor = e->x();
510 int Ycursor = e->y();
512 if ( ! checkRect.contains( e->x(), e->y() ) ) {
513 if ( e->x() < checkRect.left() ) Xcursor = checkRect.left();
514 if ( e->x() > checkRect.right() ) Xcursor = checkRect.right();
515 if ( e->y() < checkRect.top() ) Ycursor = checkRect.top();
516 if ( e->y() > checkRect.bottom() ) Ycursor = checkRect.bottom();
519 Xcursor -= leftPadding();
520 Ycursor -= topPadding();
522 QPixmap buffer2( *buffer );
523 QPainter p;
524 p.begin( &buffer2 );
525 p.translate( leftPadding(), topPadding() );
526 p.setPen( QPen( "grey", 1, SolidLine ) );
527 p.drawLine( Xcursor, 0, Xcursor, PixRect.height() );
528 p.drawLine( 0, Ycursor, PixRect.width(), Ycursor );
529 p.end();
530 bitBlt( this, 0, 0, &buffer2 );
533 void AVTPlotWidget::paintEvent( QPaintEvent */*e*/ ) {
534 QPainter p;
536 p.begin( buffer );
537 p.fillRect( 0, 0, width(), height(), bgColor() );
539 p.translate( leftPadding(), topPadding() );
541 int pW = PixRect.width();
542 int pH = PixRect.height();
544 //draw daytime sky if the Sun rises for the current date/location
545 //(when Sun does not rise, SunSet = -1.0)
546 if ( SunSet != -1.0 ) {
547 //If Sun does not set, then just fill the daytime sky color
548 if ( SunSet == 1.0 ) {
549 p.fillRect( 0, 0, pW, int(0.5*pH), QColor( 0, 100, 200 ) );
550 } else {
551 //Display centered on midnight, so need to modulate dawn/dusk by 0.5
552 int dawn = int(pW*(0.5 + SunRise));
553 int dusk = int(pW*(SunSet - 0.5));
554 p.fillRect( 0, 0, dusk, int(0.5*pH), QColor( 0, 100, 200 ) );
555 p.fillRect( dawn, 0, pW - dawn, int(0.5*pH), QColor( 0, 100, 200 ) );
557 //draw twilight gradients
558 unsigned short int W = 40;
559 for ( unsigned short int i=0; i<W; ++i ) {
560 p.setPen( QColor( 0, int(100*i/W), 200*i/W ) );
561 p.drawLine( dusk - (i-20), 0, dusk - (i-20), pH );
562 p.drawLine( dawn + (i-20), 0, dawn + (i-20), pH );
567 //draw ground
568 p.fillRect( 0, int(0.5*pH), pW, int(0.5*pH), QColor( "#002200" ) );
570 drawBox( &p );
571 drawObjects( &p );
573 //Add vertical line indicating "now"
574 QTime t = QTime::currentTime();
575 double x = 12.0 + t.hour() + t.minute()/60.0 + t.second()/3600.0;
576 while ( x > 24.0 ) x -= 24.0;
577 int ix = int(x*pW/24.0); //convert to screen pixel coords
578 p.setPen( QPen( "white", 2, DotLine ) );
579 p.drawLine( ix, 0, ix, pH );
581 p.end();
583 bitBlt( this, 0, 0, buffer );
586 #include "altvstime.moc"