You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
tdeedu/kstars/kstars/tools/altvstime.cpp

590 lines
19 KiB

/***************************************************************************
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 <tqlayout.h>
#include <tqpainter.h>
#include <klocale.h>
#include <klineedit.h>
#include <klistbox.h>
#include <kpushbutton.h>
#include <kdialogbase.h>
#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->tqsetSizePolicy( 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->tqrepaint(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->tqrepaint();
}
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; i<View->objectCount(); ++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<TQT_BASE_OBJECT_NAME*>(sender()))->name() == TQString( "nameBox" ) ) avtUI->addButton->setFocus();
if ( TQT_TQOBJECT(const_cast<TQT_BASE_OBJECT_NAME*>(sender()))->name() == TQString( "raBox" ) ) avtUI->decBox->setFocus();
if ( TQT_TQOBJECT(const_cast<TQT_BASE_OBJECT_NAME*>(sender()))->name() == TQString( "decbox" ) ) avtUI->addButton->setFocus();
if ( TQT_TQOBJECT(const_cast<TQT_BASE_OBJECT_NAME*>(sender()))->name() == TQString( "longBox" ) ) avtUI->latBox->setFocus();
if ( TQT_TQOBJECT(const_cast<TQT_BASE_OBJECT_NAME*>(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->tqrepaint();
}
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->tqrepaint();
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::tqcurrentDateTime();
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<W; ++i ) {
p.setPen( TQColor( 0, int(100*i/W), 200*i/W ) );
p.drawLine( dusk - (i-20), 0, dusk - (i-20), pH );
p.drawLine( dawn + (i-20), 0, dawn + (i-20), pH );
}
}
}
//draw ground
p.fillRect( 0, int(0.5*pH), pW, int(0.5*pH), TQColor( "#002200" ) );
drawBox( &p );
drawObjects( &p );
//Add vertical line indicating "now"
TQTime t = TQTime::currentTime();
double x = 12.0 + t.hour() + t.minute()/60.0 + t.second()/3600.0;
while ( x > 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"