/*************************************************************************** altvstime.cpp - description ------------------- begin : wed nov 17 08:05:11 CET 2002 copyright : (C) 2002-2003 by Pablo de Vicente email : vicente@oan.es ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include "altvstime.h" #include "altvstimeui.h" #include "dms.h" #include "dmsbox.h" #include "kstars.h" #include "kstarsdata.h" #include "skypoint.h" #include "skyobject.h" #include "skyobjectname.h" #include "ksnumbers.h" #include "objectnamelist.h" #include "simclock.h" #include "finddialog.h" #include "locationdialog.h" #include "kstarsdatetime.h" #include "libtdeedu/extdate/extdatetimeedit.h" AltVsTime::AltVsTime( TQWidget* parent) : KDialogBase( KDialogBase::Plain, i18n( "Altitude vs. Time" ), Close, Close, parent ) { ks = (KStars*) parent; TQFrame *page = plainPage(); setMainWidget(page); topLayout = new TQVBoxLayout( page, 0, spacingHint() ); View = new AVTPlotWidget( -12.0, 12.0, -90.0, 90.0, page ); View->setMinimumSize( 400, 400 ); View->setSizePolicy( TQSizePolicy::MinimumExpanding, TQSizePolicy::MinimumExpanding ); View->setXAxisType( KStarsPlotWidget::TIME ); View->setYAxisType( KStarsPlotWidget::ANGLE ); View->setShowGrid( false ); View->setXAxisLabel( i18n( "Local Time" ) ); View->setXAxisLabel2( i18n( "Local Sidereal Time" ) ); View->setYAxisLabel( i18n( "the angle of an object above (or below) the horizon", "Altitude" ) ); avtUI = new AltVsTimeUI( page ); avtUI->raBox->setDegType( false ); avtUI->decBox->setDegType( true ); //POST-3.2 //Doesn't make sense to manually adjust long/lat unless we can modify TZ also avtUI->longBox->setReadOnly( true ); avtUI->latBox->setReadOnly( true ); topLayout->addWidget( View ); topLayout->addWidget( avtUI ); geo = ks->geo(); DayOffset = 0; showCurrentDate(); if ( getDate().time().hour() > 12 ) DayOffset = 1; avtUI->longBox->show( geo->lng() ); avtUI->latBox->show( geo->lat() ); computeSunRiseSetTimes(); setLSTLimits(); View->updateTickmarks(); connect( avtUI->browseButton, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotBrowseObject() ) ); connect( avtUI->cityButton, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotChooseCity() ) ); connect( avtUI->updateButton, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotUpdateDateLoc() ) ); connect( avtUI->clearButton, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotClear() ) ); connect( avtUI->addButton, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotAddSource() ) ); connect( avtUI->nameBox, TQT_SIGNAL( returnPressed() ), this, TQT_SLOT( slotAddSource() ) ); connect( avtUI->raBox, TQT_SIGNAL( returnPressed() ), this, TQT_SLOT( slotAddSource() ) ); connect( avtUI->decBox, TQT_SIGNAL( returnPressed() ), this, TQT_SLOT( slotAddSource() ) ); connect( avtUI->clearFieldsButton, TQT_SIGNAL( clicked() ), this, TQT_SLOT( slotClearBoxes() ) ); connect( avtUI->longBox, TQT_SIGNAL( returnPressed() ), this, TQT_SLOT( slotAdvanceFocus() ) ); connect( avtUI->latBox, TQT_SIGNAL( returnPressed() ), this, TQT_SLOT( slotAdvanceFocus() ) ); connect( avtUI->PlotList, TQT_SIGNAL( highlighted(int) ), this, TQT_SLOT( slotHighlight() ) ); pList.setAutoDelete(FALSE); deleteList.setAutoDelete(TRUE); //needed for skypoints which may be created in this class //ther edit boxes should not pass on the return key! avtUI->nameBox->setTrapReturnKey( true ); avtUI->raBox->setTrapReturnKey( true ); avtUI->decBox->setTrapReturnKey( true ); setMouseTracking( true ); } AltVsTime::~AltVsTime() { // no need to delete child widgets, TQt does it all for us } void AltVsTime::slotAddSource(void) { bool objFound( false ); //First, attempt to find the object name in the list of known objects if ( ! avtUI->nameBox->text().isEmpty() ) { ObjectNameList &ObjNames = ks->data()->ObjNames; TQString text = avtUI->nameBox->text().lower(); for( SkyObjectName *name = ObjNames.first( text ); name; name = ObjNames.next() ) { if ( name->text().lower() == text ) { //object found SkyObject *o = name->skyObject(); processObject( o ); objFound = true; break; } } if ( !objFound ) kdDebug() << "No object named " << avtUI->nameBox->text() << " found." << endl; } //Next, if the name, RA, and Dec fields are all filled, construct a new skyobject //with these parameters if ( !objFound && ! avtUI->nameBox->text().isEmpty() && ! avtUI->raBox->text().isEmpty() && ! avtUI->decBox->text().isEmpty() ) { bool ok( true ); dms newRA( 0.0 ), newDec( 0.0 ); newRA = avtUI->raBox->createDms( false, &ok ); if ( ok ) newDec = avtUI->decBox->createDms( true, &ok ); //If the epochName is blank (or any non-double), we assume J2000 //Otherwise, precess to J2000. KStarsDateTime dt; dt.setFromEpoch( getEpoch( avtUI->epochName->text() ) ); long double jd = dt.djd(); if ( jd != J2000 ) { SkyPoint ptest( newRA, newDec ); ptest.precessFromAnyEpoch( jd, J2000 ); newRA.setH( ptest.ra()->Hours() ); newDec.setD( ptest.dec()->Degrees() ); } //make sure the coords do not already exist from another object bool found(false); for ( SkyPoint *p = pList.first(); p; p = pList.next() ) { //within an arcsecond? if ( fabs( newRA.Degrees() - p->ra()->Degrees() ) < 0.0003 && fabs( newDec.Degrees() - p->dec()->Degrees() ) < 0.0003 ) { found = true; break; } } if ( found ) { kdDebug() << "This point is already displayed; I will not duplicate it." << endl; ok = false; } if ( ok ) { SkyObject *obj = new SkyObject( 8, newRA, newDec, 1.0, avtUI->nameBox->text() ); deleteList.append( obj ); //this object will be deleted when window is destroyed processObject( obj ); } //If the Ra and Dec boxes are filled, but the name field is empty, //move input focus to nameBox` } else if ( avtUI->nameBox->text().isEmpty() && ! avtUI->raBox->text().isEmpty() && ! avtUI->decBox->text().isEmpty() ) { avtUI->nameBox->TQWidget::setFocus(); //nameBox is empty, and one of the ra or dec fields is empty. Move input focus to empty coord box } else if ( avtUI->raBox->text().isEmpty() ) { avtUI->raBox->TQWidget::setFocus(); } else if ( avtUI->decBox->text().isEmpty() ) { avtUI->decBox->TQWidget::setFocus(); } View->repaint(false); } //Use find dialog to choose an object void AltVsTime::slotBrowseObject(void) { FindDialog fd(ks); if ( fd.exec() == TQDialog::Accepted ) { SkyObject *o = fd.currentItem()->objName()->skyObject(); processObject( o ); } View->repaint(); } void AltVsTime::processObject( SkyObject *o, bool forceAdd ) { KSNumbers *num = new KSNumbers( getDate().djd() ); KSNumbers *oldNum = 0; //If the object is in the solar system, recompute its position for the given epochLabel if ( o && o->isSolarSystem() ) { oldNum = new KSNumbers( ks->data()->ut().djd() ); o->updateCoords( num, true, geo->lat(), ks->LST() ); } //precess coords to target epoch o->updateCoords( num ); //If this point is not in list already, add it to list bool found(false); for ( SkyPoint *p = pList.first(); p; p = pList.next() ) { if ( o->ra()->Degrees() == p->ra()->Degrees() && o->dec()->Degrees() == p->dec()->Degrees() ) { found = true; break; } } if ( found && !forceAdd ) kdDebug() << "This point is already displayed; I will not duplicate it." << endl; else { pList.append( (SkyPoint*)o ); //make sure existing curves are thin and red for ( int i=0; i < View->objectCount(); ++i ) { KPlotObject *obj = View->object( i ); if ( obj->size() == 2 ) { obj->setColor( "red" ); obj->setSize( 1 ); } } //add new curve with width=2, and color=white KPlotObject *po = new KPlotObject( "", "white", KPlotObject::CURVE, 2, KPlotObject::SOLID ); for ( double h=-12.0; h<=12.0; h+=0.5 ) { po->addPoint( new DPoint( h, findAltitude( o, h ) ) ); } View->addObject( po ); avtUI->PlotList->insertItem( o->translatedName() ); avtUI->PlotList->setCurrentItem( avtUI->PlotList->count() - 1 ); avtUI->raBox->showInHours(o->ra() ); avtUI->decBox->showInDegrees(o->dec() ); avtUI->nameBox->setText(o->translatedName() ); //Set epochName to epoch shown in date tab avtUI->epochName->setText( TQString().setNum( getDate().epoch() ) ); } kdDebug() << "Currently, there are " << View->objectCount() << " objects displayed." << endl; //restore original position if ( o->isSolarSystem() ) { o->updateCoords( oldNum, true, ks->geo()->lat(), ks->LST() ); delete oldNum; } o->EquatorialToHorizontal( ks->LST(), ks->geo()->lat() ); delete num; } double AltVsTime::findAltitude( SkyPoint *p, double hour ) { hour += 24.0*(double)DayOffset; //getDate converts the user-entered local time to UT KStarsDateTime ut = getDate().addSecs( hour*3600.0 ); dms LST = geo->GSTtoLST( ut.gst() ); p->EquatorialToHorizontal( &LST, geo->lat() ); return p->alt()->Degrees(); } void AltVsTime::slotHighlight(void) { int iPlotList = avtUI->PlotList->currentItem(); //highlight the curve of the selected object for ( int i=0; iobjectCount(); ++i ) { KPlotObject *obj = View->object( i ); if ( i == iPlotList ) { obj->setSize( 2 ); obj->setColor( "white" ); } else { obj->setSize( 1 ); obj->setColor( "red" ); } } View->update(); int index = 0; for ( SkyPoint *p = pList.first(); p; p = pList.next() ) { if ( index == iPlotList ) { avtUI->raBox->showInHours(p->ra() ); avtUI->decBox->showInDegrees(p->dec() ); avtUI->nameBox->setText(avtUI->PlotList->currentText() ); } ++index; } } //move input focus to the next logical widget void AltVsTime::slotAdvanceFocus(void) { if ( TQT_TQOBJECT(const_cast(sender()))->name() == TQString( "nameBox" ) ) avtUI->addButton->setFocus(); if ( TQT_TQOBJECT(const_cast(sender()))->name() == TQString( "raBox" ) ) avtUI->decBox->setFocus(); if ( TQT_TQOBJECT(const_cast(sender()))->name() == TQString( "decbox" ) ) avtUI->addButton->setFocus(); if ( TQT_TQOBJECT(const_cast(sender()))->name() == TQString( "longBox" ) ) avtUI->latBox->setFocus(); if ( TQT_TQOBJECT(const_cast(sender()))->name() == TQString( "latBox" ) ) avtUI->updateButton->setFocus(); } void AltVsTime::slotClear(void) { if ( pList.count() ) pList.clear(); if ( deleteList.count() ) deleteList.clear(); avtUI->PlotList->clear(); avtUI->nameBox->clear(); avtUI->raBox->clear(); avtUI->decBox->clear(); avtUI->epochName->clear(); View->clearObjectList(); View->repaint(); } void AltVsTime::slotClearBoxes(void) { avtUI->nameBox->clear(); avtUI->raBox->clear() ; avtUI->decBox->clear(); avtUI->epochName->clear(); } void AltVsTime::computeSunRiseSetTimes() { //Determine the time of sunset and sunrise for the desired date and location //expressed as doubles, the fraction of a full day. KStarsDateTime today = getDate(); SkyObject *oSun = (SkyObject*) ks->data()->PCat->planetSun(); double sunRise = -1.0 * oSun->riseSetTime( today.djd() + 1.0, geo, true ).secsTo(TQTime()) / 86400.0; double sunSet = -1.0 * oSun->riseSetTime( today.djd(), geo, false ).secsTo(TQTime()) / 86400.0; //check to see if Sun is circumpolar //requires temporary repositioning of Sun to target date KSNumbers *num = new KSNumbers( today.djd() ); KSNumbers *oldNum = new KSNumbers( ks->data()->ut().djd() ); dms LST = geo->GSTtoLST( getDate().gst() ); oSun->updateCoords( num, true, geo->lat(), &LST ); if ( oSun->checkCircumpolar( geo->lat() ) ) { if ( oSun->alt()->Degrees() > 0.0 ) { //Circumpolar, signal it this way: sunRise = 0.0; sunSet = 1.0; } else { //never rises, signal it this way: sunRise = 0.0; sunSet = -1.0; } } //Notify the View about new sun rise/set times: View->setSunRiseSetTimes( sunRise, sunSet ); //Restore Sun coordinates: oSun->updateCoords( oldNum, true, ks->geo()->lat(), ks->LST() ); oSun->EquatorialToHorizontal( ks->LST(), ks->geo()->lat() ); delete num; delete oldNum; } void AltVsTime::slotUpdateDateLoc(void) { KStarsDateTime today = getDate(); KSNumbers *num = new KSNumbers( today.djd() ); KSNumbers *oldNum = 0; dms LST = geo->GSTtoLST( today.gst() ); //First determine time of sunset and sunrise computeSunRiseSetTimes(); for ( unsigned int i = 0; i < avtUI->PlotList->count(); ++i ) { TQString oName = avtUI->PlotList->text( i ).lower(); ObjectNameList &ObjNames = ks->data()->ObjNames; bool objFound(false); for( SkyObjectName *name = ObjNames.first( oName ); name; name = ObjNames.next() ) { if ( name->text().lower() == oName ) { //object found SkyObject *o = name->skyObject(); //If the object is in the solar system, recompute its position for the given date if ( o->isSolarSystem() ) { oldNum = new KSNumbers( ks->data()->ut().djd() ); o->updateCoords( num, true, geo->lat(), &LST ); } //precess coords to target epoch o->updateCoords( num ); //update pList entry pList.replace( i, (SkyPoint*)o ); KPlotObject *po = new KPlotObject( "", "white", KPlotObject::CURVE, 1, KPlotObject::SOLID ); for ( double h=-12.0; h<=12.0; h+=0.5 ) { po->addPoint( new DPoint( h, findAltitude( o, h ) ) ); } View->replaceObject( i, po ); //restore original position if ( o->isSolarSystem() ) { o->updateCoords( oldNum, true, ks->data()->geo()->lat(), ks->LST() ); delete oldNum; oldNum = 0; } o->EquatorialToHorizontal( ks->LST(), ks->data()->geo()->lat() ); objFound = true; break; } } if ( ! objFound ) { //assume unfound object is a custom object pList.at(i)->updateCoords( num ); //precess to desired epoch KPlotObject *po = new KPlotObject( "", "white", KPlotObject::CURVE, 1, KPlotObject::SOLID ); for ( double h=-12.0; h<=12.0; h+=0.5 ) { po->addPoint( new DPoint( h, findAltitude( pList.at(i), h ) ) ); } View->replaceObject( i, po ); } } if ( getDate().time().hour() > 12 ) DayOffset = 1; else DayOffset = 0; setLSTLimits(); slotHighlight(); View->repaint(); delete num; } void AltVsTime::slotChooseCity(void) { LocationDialog ld(ks); if ( ld.exec() == TQDialog::Accepted ) { GeoLocation *newGeo = ld.selectedCity(); if ( newGeo ) { geo = newGeo; avtUI->latBox->showInDegrees( geo->lat() ); avtUI->longBox->showInDegrees( geo->lng() ); } } } int AltVsTime::currentPlotListItem() const { return avtUI->PlotList->currentItem(); } void AltVsTime::setLSTLimits(void) { //UT at noon on target date KStarsDateTime ut = getDate().addSecs( ((double)DayOffset + 0.5)*86400. ); dms lst = geo->GSTtoLST( ut.gst() ); View->setSecondaryLimits( lst.Hours(), lst.Hours() + 24.0, -90.0, 90.0 ); } void AltVsTime::showCurrentDate (void) { KStarsDateTime dt = KStarsDateTime::currentDateTime(); if ( dt.time() > TQTime( 12, 0, 0 ) ) dt = dt.addDays( 1 ); avtUI->dateBox->setDate( dt.date() ); } KStarsDateTime AltVsTime::getDate (void) { //convert midnight local time to UT: KStarsDateTime dt( avtUI->dateBox->date(), TQTime() ); dt = geo->LTtoUT( dt ); return dt; } double AltVsTime::getEpoch (TQString eName) { //If Epoch field not a double, assume J2000 bool ok(false); double epoch = eName.toDouble(&ok); if ( !ok ) { kdDebug() << "Invalid Epoch. Assuming 2000.0." << endl; return 2000.0; } return epoch; } AVTPlotWidget::AVTPlotWidget( double x1, double x2, double y1, double y2, TQWidget *parent, const char* name ) : KStarsPlotWidget( x1, x2, y1, y2, parent, name ) { //Default SunRise/SunSet values SunRise = 0.25; SunSet = 0.75; } void AVTPlotWidget::mousePressEvent( TQMouseEvent *e ) { mouseMoveEvent( e ); } void AVTPlotWidget::mouseMoveEvent( TQMouseEvent *e ) { TQRect checkRect( leftPadding(), topPadding(), PixRect.width(), PixRect.height() ); int Xcursor = e->x(); int Ycursor = e->y(); if ( ! checkRect.contains( e->x(), e->y() ) ) { if ( e->x() < checkRect.left() ) Xcursor = checkRect.left(); if ( e->x() > checkRect.right() ) Xcursor = checkRect.right(); if ( e->y() < checkRect.top() ) Ycursor = checkRect.top(); if ( e->y() > checkRect.bottom() ) Ycursor = checkRect.bottom(); } Xcursor -= leftPadding(); Ycursor -= topPadding(); TQPixmap buffer2( *buffer ); TQPainter p; p.begin( &buffer2 ); p.translate( leftPadding(), topPadding() ); p.setPen( TQPen( "grey", 1, SolidLine ) ); p.drawLine( Xcursor, 0, Xcursor, PixRect.height() ); p.drawLine( 0, Ycursor, PixRect.width(), Ycursor ); p.end(); bitBlt( this, 0, 0, &buffer2 ); } void AVTPlotWidget::paintEvent( TQPaintEvent */*e*/ ) { TQPainter p; p.begin( buffer ); p.fillRect( 0, 0, width(), height(), bgColor() ); p.translate( leftPadding(), topPadding() ); int pW = PixRect.width(); int pH = PixRect.height(); //draw daytime sky if the Sun rises for the current date/location //(when Sun does not rise, SunSet = -1.0) if ( SunSet != -1.0 ) { //If Sun does not set, then just fill the daytime sky color if ( SunSet == 1.0 ) { p.fillRect( 0, 0, pW, int(0.5*pH), TQColor( 0, 100, 200 ) ); } else { //Display centered on midnight, so need to modulate dawn/dusk by 0.5 int dawn = int(pW*(0.5 + SunRise)); int dusk = int(pW*(SunSet - 0.5)); p.fillRect( 0, 0, dusk, int(0.5*pH), TQColor( 0, 100, 200 ) ); p.fillRect( dawn, 0, pW - dawn, int(0.5*pH), TQColor( 0, 100, 200 ) ); //draw twilight gradients unsigned short int W = 40; for ( unsigned short int i=0; i 24.0 ) x -= 24.0; int ix = int(x*pW/24.0); //convert to screen pixel coords p.setPen( TQPen( "white", 2, DotLine ) ); p.drawLine( ix, 0, ix, pH ); p.end(); bitBlt( this, 0, 0, buffer ); } #include "altvstime.moc"