• Skip to content
  • Skip to link menu
KDE 3.5 API Reference
  • KDE API Reference
  • API Reference
  • Sitemap
  • Contact Us
 

kstars

skymap.cpp

Go to the documentation of this file.
00001 /***************************************************************************
00002                           skymap.cpp  -  K Desktop Planetarium
00003                              -------------------
00004     begin                : Sat Feb 10 2001
00005     copyright            : (C) 2001 by Jason Harris
00006     email                : jharris@30doradus.org
00007  ***************************************************************************/
00008 
00009 /***************************************************************************
00010  *                                                                         *
00011  *   This program is free software; you can redistribute it and/or modify  *
00012  *   it under the terms of the GNU General Public License as published by  *
00013  *   the Free Software Foundation; either version 2 of the License, or     *
00014  *   (at your option) any later version.                                   *
00015  *                                                                         *
00016  ***************************************************************************/
00017 
00018 #include <kapplication.h>
00019 #include <kconfig.h>
00020 #include <kiconloader.h>
00021 #include <kstatusbar.h>
00022 #include <kmessagebox.h>
00023 #include <kaction.h>
00024 #include <kstandarddirs.h>
00025 
00026 #include <qmemarray.h>
00027 #include <qpointarray.h>
00028 #include <qcursor.h>
00029 #include <qbitmap.h>
00030 #include <qpainter.h>
00031 
00032 #include <math.h>
00033 #include <stdlib.h>
00034 #include <unistd.h>
00035 
00036 #include "skymap.h"
00037 #include "Options.h"
00038 #include "kstars.h"
00039 #include "kstarsdata.h"
00040 #include "imageviewer.h"
00041 #include "infoboxes.h"
00042 #include "detaildialog.h"
00043 #include "addlinkdialog.h"
00044 #include "kspopupmenu.h"
00045 #include "simclock.h"
00046 #include "skyobject.h"
00047 #include "deepskyobject.h"
00048 #include "ksmoon.h"
00049 #include "ksasteroid.h"
00050 #include "kscomet.h"
00051 #include "starobject.h"
00052 #include "customcatalog.h"
00053 
00054 SkyMap::SkyMap(KStarsData *d, QWidget *parent, const char *name )
00055     : QWidget (parent,name), computeSkymap(true), angularDistanceMode(false),
00056         ksw(0), data(d), pmenu(0), sky(0), sky2(0), IBoxes(0), 
00057         ClickedObject(0), FocusObject(0), TransientObject(0),
00058         starpix(0), pts(0), sp(0)
00059 {
00060     if ( parent ) ksw = (KStars*) parent->parent();
00061     else ksw = 0;
00062     
00063     pts = new QPointArray( 2000 );  // points for milkyway and horizon
00064     sp = new SkyPoint();            // needed by coordinate grid
00065 
00066     ZoomRect = QRect();
00067 
00068     setDefaultMouseCursor();    // set the cross cursor
00069 
00070     // load the pixmaps of stars
00071     starpix = new StarPixmap( data->colorScheme()->starColorMode(), data->colorScheme()->starColorIntensity() );
00072 
00073     setBackgroundColor( QColor( data->colorScheme()->colorNamed( "SkyColor" ) ) );
00074     setBackgroundMode( QWidget::NoBackground );
00075     setFocusPolicy( QWidget::StrongFocus );
00076     setMinimumSize( 380, 250 );
00077     setSizePolicy( QSizePolicy( QSizePolicy::Expanding, QSizePolicy::Expanding ) );
00078 
00079     setMouseTracking (true); //Generate MouseMove events!
00080     midMouseButtonDown = false;
00081     mouseButtonDown = false;
00082     slewing = false;
00083     clockSlewing = false;
00084 
00085     ClickedObject = NULL;
00086     FocusObject = NULL;
00087 
00088     sky = new QPixmap();
00089     sky2 = new QPixmap();
00090     pmenu = new KSPopupMenu( ksw );
00091     
00092     //Initialize Transient label stuff
00093     TransientTimeout = 100; //fade label color every 0.2 sec
00094     connect( &HoverTimer, SIGNAL( timeout() ), this, SLOT( slotTransientLabel() ) );
00095     connect( &TransientTimer, SIGNAL( timeout() ), this, SLOT( slotTransientTimeout() ) );
00096     
00097     IBoxes = new InfoBoxes( Options::windowWidth(), Options::windowHeight(),
00098             Options::positionTimeBox(), Options::shadeTimeBox(),
00099             Options::positionGeoBox(), Options::shadeGeoBox(),
00100             Options::positionFocusBox(), Options::shadeFocusBox(),
00101             data->colorScheme()->colorNamed( "BoxTextColor" ),
00102             data->colorScheme()->colorNamed( "BoxGrabColor" ),
00103             data->colorScheme()->colorNamed( "BoxBGColor" ) );
00104 
00105     IBoxes->showTimeBox( Options::showTimeBox() );
00106     IBoxes->showFocusBox( Options::showFocusBox() );
00107     IBoxes->showGeoBox( Options::showGeoBox() );
00108     IBoxes->timeBox()->setAnchorFlag( Options::stickyTimeBox() );
00109     IBoxes->geoBox()->setAnchorFlag( Options::stickyGeoBox() );
00110     IBoxes->focusBox()->setAnchorFlag( Options::stickyFocusBox() );
00111 
00112     IBoxes->geoChanged( data->geo() );
00113 
00114     connect( IBoxes->timeBox(),  SIGNAL( shaded(bool) ), data, SLOT( saveTimeBoxShaded(bool) ) );
00115     connect( IBoxes->geoBox(),   SIGNAL( shaded(bool) ), data, SLOT( saveGeoBoxShaded(bool) ) );
00116     connect( IBoxes->focusBox(), SIGNAL( shaded(bool) ), data, SLOT( saveFocusBoxShaded(bool) ) );
00117     connect( IBoxes->timeBox(),  SIGNAL( moved(QPoint) ), data, SLOT( saveTimeBoxPos(QPoint) ) );
00118     connect( IBoxes->geoBox(),   SIGNAL( moved(QPoint) ), data, SLOT( saveGeoBoxPos(QPoint) ) );
00119     connect( IBoxes->focusBox(), SIGNAL( moved(QPoint) ), data, SLOT( saveFocusBoxPos(QPoint) ) );
00120 
00121     connect( this, SIGNAL( destinationChanged() ), this, SLOT( slewFocus() ) );
00122 
00123     //Initialize Refraction correction lookup table arrays.  RefractCorr1 is for calculating
00124     //the apparent altitude from the true altitude, and RefractCorr2 is for the reverse.
00125     for ( unsigned int index = 0; index <184; ++index ) {
00126         double alt = -1.75 + index*0.5;  //start at -1.75 degrees to get midpoint value for each interval.
00127 
00128         RefractCorr1[index] = 1.02 / tan( dms::PI*( alt + 10.3/(alt + 5.11) )/180.0 ) / 60.0; //correction in degrees.
00129         RefractCorr2[index] = -1.0 / tan( dms::PI*( alt + 7.31/(alt + 4.4) )/180.0 ) / 60.0;
00130     }
00131 }
00132 
00133 SkyMap::~SkyMap() {
00134     delete starpix;
00135     delete pts;
00136     delete sp;
00137     delete sky;
00138     delete sky2;
00139     delete pmenu;
00140     delete IBoxes;
00141 
00142 //Deprecated...DeepSkyObject dtor now handles this itself.
00143 /*//delete any remaining object Image pointers
00144     for ( DeepSkyObject *obj = data->deepSkyListMessier.first(); obj; obj = data->deepSkyListMessier.next() ) {
00145         if ( obj->image() ) obj->deleteImage();
00146   }
00147     for ( DeepSkyObject *obj = data->deepSkyListNGC.first(); obj; obj = data->deepSkyListNGC.next() ) {
00148         if ( obj->image() ) obj->deleteImage();
00149   }
00150     for ( DeepSkyObject *obj = data->deepSkyListIC.first(); obj; obj = data->deepSkyListIC.next() ) {
00151         if ( obj->image() ) obj->deleteImage();
00152   }
00153     for ( DeepSkyObject *obj = data->deepSkyListOther.first(); obj; obj = data->deepSkyListOther.next() ) {
00154         if ( obj->image() ) obj->deleteImage();
00155   }*/
00156 }
00157 
00158 void SkyMap::setGeometry( int x, int y, int w, int h ) {
00159     QWidget::setGeometry( x, y, w, h );
00160     sky->resize( w, h );
00161     sky2->resize( w, h );
00162 }
00163 
00164 void SkyMap::setGeometry( const QRect &r ) {
00165     QWidget::setGeometry( r );
00166     sky->resize( r.width(), r.height() );
00167     sky2->resize( r.width(), r.height() );
00168 }
00169 
00170 
00171 void SkyMap::showFocusCoords( bool coordsOnly ) {
00172     if ( ! coordsOnly ) {
00173         //display object info in infoBoxes
00174         QString oname;
00175         oname = i18n( "nothing" );
00176         if ( focusObject() != NULL && Options::isTracking() ) 
00177             oname = focusObject()->translatedLongName();
00178         
00179         infoBoxes()->focusObjChanged(oname);
00180     }
00181 
00182     if ( Options::useAltAz() && Options::useRefraction() ) {
00183         SkyPoint corrFocus( *(focus()) );
00184         corrFocus.setAlt( refract( focus()->alt(), false ) );
00185         corrFocus.HorizontalToEquatorial( data->LST, data->geo()->lat() );
00186         corrFocus.setAlt( refract( focus()->alt(), true ) );
00187         infoBoxes()->focusCoordChanged( &corrFocus );
00188     } else {
00189         infoBoxes()->focusCoordChanged( focus() );
00190     }
00191 }
00192 
00193 SkyObject* SkyMap::objectNearest( SkyPoint *p ) {
00194     double r0 = 200.0/Options::zoomFactor();  //the maximum search radius
00195     double rmin = r0;
00196 
00197     //Search stars database for nearby object.
00198     double rstar_min = r0;
00199     double starmag_min = 20.0;      //absurd initial value
00200     int istar_min = -1;
00201 
00202     if ( Options::showStars() ) { //Can only click on a star if it's being drawn!
00203 
00204         //test RA and dec to see if this star is roughly nearby
00205 
00206         for ( register unsigned int i=0; i<data->starList.count(); ++i ) {
00207             SkyObject *test = (SkyObject *)data->starList.at(i);
00208 
00209             double dRA = test->ra()->Hours() - p->ra()->Hours();
00210             double dDec = test->dec()->Degrees() - p->dec()->Degrees();
00211             //determine angular distance between this object and mouse cursor
00212             double f = 15.0*cos( test->dec()->radians() );
00213             double r = f*f*dRA*dRA + dDec*dDec; //no need to take sqrt, we just want to ID smallest value.
00214             if (r < r0 && test->mag() < starmag_min ) {
00215                 istar_min = i;
00216                 rstar_min = r;
00217                 starmag_min = test->mag();
00218             }
00219         }
00220     }
00221 
00222     //Next, find the nearest solar system body within r0
00223     double r = 0.0;
00224     double rsolar_min = r0;
00225     SkyObject *solarminobj = NULL;
00226 
00227     if ( Options::showPlanets() )
00228         solarminobj = data->PCat->findClosest( p, r );
00229 
00230     if ( r < r0 ) {
00231         rsolar_min = r;
00232     } else {
00233         solarminobj = NULL;
00234     }
00235 
00236     //Moon
00237     if ( Options::showMoon() ) {
00238         double dRA = data->Moon->ra()->Hours() - p->ra()->Hours();
00239         double dDec = data->Moon->dec()->Degrees() - p->dec()->Degrees();
00240         double f = 15.0*cos( data->Moon->dec()->radians() );
00241         r = f*f*dRA*dRA + dDec*dDec; //no need to take sqrt, we just want to ID smallest value.
00242         if (r < rsolar_min) {
00243             solarminobj= data->Moon;
00244             rsolar_min = r;
00245         }
00246     }
00247 
00248     //Asteroids
00249     if ( Options::showAsteroids() ) {
00250         for ( KSAsteroid *ast = data->asteroidList.first(); ast; ast = data->asteroidList.next() ) {
00251             //test RA and dec to see if this object is roughly nearby
00252             double dRA = ast->ra()->Hours() - p->ra()->Hours();
00253             double dDec = ast->dec()->Degrees() - p->dec()->Degrees();
00254             double f = 15.0*cos( ast->dec()->radians() );
00255             double r = f*f*dRA*dRA + dDec*dDec; //no need to take sqrt, we just want to ID smallest value.
00256             if ( r < rsolar_min && ast->mag() < Options::magLimitAsteroid() ) {
00257                 solarminobj = ast;
00258                 rsolar_min = r;
00259             }
00260         }
00261     }
00262 
00263     //Comets
00264     if ( Options::showComets() ) {
00265         for ( KSComet *com = data->cometList.first(); com; com = data->cometList.next() ) {
00266             //test RA and dec to see if this object is roughly nearby
00267             double dRA = com->ra()->Hours() - p->ra()->Hours();
00268             double dDec = com->dec()->Degrees() - p->dec()->Degrees();
00269             double f = 15.0*cos( com->dec()->radians() );
00270             double r = f*f*dRA*dRA + dDec*dDec; //no need to take sqrt, we just want to ID smallest value.
00271             if ( r < rsolar_min ) {
00272                 solarminobj = com;
00273                 rsolar_min = r;
00274             }
00275         }
00276     }
00277 
00278     //Next, search for nearest deep-sky object within r0
00279     double rmess_min = r0;
00280     double rngc_min = r0;
00281     double ric_min = r0;
00282     double rother_min = r0;
00283     int imess_min = -1;
00284     int ingc_min = -1;
00285     int iic_min = -1;
00286     int iother_min = -1;
00287 
00288     for ( DeepSkyObject *o = data->deepSkyList.first(); o; o = data->deepSkyList.next() ) {
00289         bool checkObject = false;
00290         if ( o->isCatalogM() &&
00291                 ( Options::showMessier() || Options::showMessierImages() ) ) checkObject = true;
00292         if ( o->isCatalogNGC() && Options::showNGC() ) checkObject = true;
00293         if ( o->isCatalogIC() && Options::showIC() ) checkObject = true;
00294         if ( o->catalog().isEmpty() && Options::showOther() ) checkObject = true;
00295 
00296         if ( checkObject ) {
00297             //test RA and dec to see if this object is roughly nearby
00298             double dRA = o->ra()->Hours() - p->ra()->Hours();
00299             double dDec = o->dec()->Degrees() - p->dec()->Degrees();
00300             double f = 15.0*cos( o->dec()->radians() );
00301             double r = f*f*dRA*dRA + dDec*dDec; //no need to take sqrt, we just want to ID smallest value.
00302             if ( o->isCatalogM() && r < rmess_min) {
00303                 imess_min = data->deepSkyList.at();
00304                 rmess_min = r;
00305             }
00306             if ( o->isCatalogNGC() && r < rngc_min) {
00307                 ingc_min = data->deepSkyList.at();
00308                 rngc_min = r;
00309             }
00310             if ( o->isCatalogIC() && r < ric_min) {
00311                 iic_min = data->deepSkyList.at();
00312                 ric_min = r;
00313             }
00314             if ( o->catalog().isEmpty() && r < rother_min) {
00315                 iother_min = data->deepSkyList.at();
00316                 rother_min = r;
00317             }
00318         }
00319     }
00320 
00321     //Next, search for nearest object within r0 among the custom catalogs
00322     double rcust_min = r0;
00323     int icust_min = -1;
00324     int icust_cat = -1;
00325 
00326     for ( register unsigned int j=0; j< data->CustomCatalogs.count(); ++j ) {
00327         if ( Options::showCatalog()[j] ) {
00328             QPtrList<SkyObject> catList = data->CustomCatalogs.at(j)->objList();
00329 
00330             for ( register unsigned int i=0; i<catList.count(); ++i ) {
00331                 //test RA and dec to see if this object is roughly nearby
00332                 SkyObject *test = (SkyObject *)catList.at(i);
00333                 double dRA = test->ra()->Hours()-p->ra()->Hours();
00334                 double dDec = test->dec()->Degrees()-p->dec()->Degrees();
00335                 double f = 15.0*cos( test->dec()->radians() );
00336                 double r = f*f*dRA*dRA + dDec*dDec; //no need to take sqrt, we just want to ID smallest value.
00337                 if (r < rcust_min) {
00338                     icust_cat = j;
00339                     icust_min = i;
00340                     rcust_min = r;
00341                 }
00342             }
00343         }
00344     }
00345 
00346     int jmin(-1);
00347     int icat(-1);
00348 
00349     //Among the objects selected within r0, prioritize the selection by catalog:
00350     //Planets, Messier, NGC, IC, stars
00351     if ( istar_min >= 0 && rstar_min < r0 ) {
00352         rmin = rstar_min;
00353         icat = 0; //set catalog to star
00354     }
00355 
00356     //IC object overrides star, unless star is twice as close as IC object
00357     if ( iic_min >= 0 && ric_min < r0 && rmin > 0.5*ric_min ) {
00358         rmin = ric_min;
00359         icat = 1; //set catalog to Deep Sky
00360         jmin = iic_min;
00361     }
00362 
00363     //NGC object overrides previous selection, unless previous is twice as close
00364     if ( ingc_min >= 0 && rngc_min < r0 && rmin > 0.5*rngc_min ) {
00365         rmin = rngc_min;
00366         icat = 1; //set catalog to Deep Sky
00367         jmin = ingc_min;
00368     }
00369 
00370     //"other" object overrides previous selection, unless previous is twice as close
00371     if ( iother_min >= 0 && rother_min < r0 && rmin > 0.5*rother_min ) {
00372         rmin = rother_min;
00373         icat = 1; //set catalog to Deep Sky
00374         jmin = iother_min;
00375     }
00376 
00377     //Messier object overrides previous selection, unless previous is twice as close
00378     if ( imess_min >= 0 && rmess_min < r0 && rmin > 0.5*rmess_min ) {
00379         rmin = rmess_min;
00380         icat = 1; //set catalog to Deep Sky
00381         jmin = imess_min;
00382     }
00383 
00384     //Custom object overrides previous selection, unless previous is twice as close
00385     if ( icust_min >= 0 && rcust_min < r0 && rmin > 0.5*rcust_min ) {
00386         rmin = rcust_min;
00387         icat = 2; //set catalog to Custom
00388     }
00389 
00390     //Solar system body overrides previous selection, unless previous selection is twice as close
00391     if ( solarminobj != NULL && rmin > 0.5*rsolar_min ) {
00392         rmin = rsolar_min;
00393         icat = 3; //set catalog to solar system
00394     }
00395 
00396     QPtrList<SkyObject> cat;
00397 
00398     switch (icat) {
00399         case 0: //star
00400             return data->starList.at(istar_min);
00401             break;
00402 
00403         case 1: //Deep-Sky Objects
00404             return data->deepSkyList.at(jmin);
00405             break;
00406 
00407         case 2: //Custom Catalog Object
00408             cat = data->CustomCatalogs.at(icust_cat)->objList();
00409             return cat.at(icust_min);
00410             break;
00411 
00412         case 3: //solar system object
00413             return solarminobj;
00414             break;
00415 
00416         default: //no object found
00417             return NULL;
00418             break;
00419     }
00420 }
00421 
00422 void SkyMap::slotTransientLabel( void ) {
00423     //This function is only called if the HoverTimer manages to timeout.
00424     //(HoverTimer is restarted with every mouseMoveEvent; so if it times 
00425     //out, that means there was no mouse movement for HOVER_INTERVAL msec.)
00426     //Identify the object nearest to the mouse cursor as the
00427     //TransientObject.  The TransientObject is automatically labeled 
00428     //in SkyMap::paintEvent().
00429     //Note that when the TransientObject pointer is not NULL, the next 
00430     //mouseMoveEvent calls fadeTransientLabel(), which will fade out the 
00431     //TransientLabel and then set TransientObject to NULL.
00432     //
00433     //Do not show a transient label if the map is in motion, or if the mouse 
00434     //pointer is below the opaque horizon, or if the object has a permanent label
00435     if ( ! slewing && ! ( Options::useAltAz() && Options::showGround() && 
00436             mousePoint()->alt()->Degrees() < 0.0 ) ) {
00437         SkyObject *so = objectNearest( mousePoint() );
00438         
00439         if ( so && ! isObjectLabeled( so ) ) {
00440             setTransientObject( so );
00441             
00442             TransientColor = data->colorScheme()->colorNamed( "UserLabelColor" );
00443             if ( TransientTimer.isActive() ) TransientTimer.stop();
00444             update();
00445         }
00446     }
00447 }
00448 
00449 
00450 //Slots
00451 
00452 void SkyMap::slotTransientTimeout( void ) {
00453     //Don't fade label if the transientObject is now the focusObject!
00454     if ( transientObject() == focusObject() && Options::useAutoLabel() ) {
00455         setTransientObject( NULL );
00456         TransientTimer.stop();
00457         return;
00458     }
00459 
00460     //to fade the labels, we will need to smoothly transition from UserLabelColor to SkyColor.
00461     QColor c1 = data->colorScheme()->colorNamed( "UserLabelColor" );
00462     QColor c2 = data->colorScheme()->colorNamed( "SkyColor" );
00463     
00464     int dRed =   ( c2.red()   - c1.red()   )/20;
00465     int dGreen = ( c2.green() - c1.green() )/20;
00466     int dBlue =  ( c2.blue()  - c1.blue()  )/20;
00467     int newRed   = TransientColor.red()   + dRed;
00468     int newGreen = TransientColor.green() + dGreen;
00469     int newBlue  = TransientColor.blue()  + dBlue;
00470     
00471     //Check to see if we have arrived at the target color (SkyColor).
00472     //If so, point TransientObject to NULL.
00473     if ( abs(newRed-c2.red()) < abs(dRed) || abs(newGreen-c2.green()) < abs(dGreen) || abs(newBlue-c2.blue()) < abs(dBlue) ) {
00474         setTransientObject( NULL );
00475         TransientTimer.stop();
00476     } else { 
00477         TransientColor.setRgb( newRed, newGreen, newBlue );
00478     }
00479 
00480     update();
00481 }
00482 
00483 void SkyMap::setFocusObject( SkyObject *o ) { 
00484     FocusObject = o; 
00485     
00486     if ( FocusObject ) 
00487         Options::setFocusObject( FocusObject->name() );
00488     else 
00489         Options::setFocusObject( i18n( "nothing" ) );
00490 }
00491 
00492 void SkyMap::slotCenter( void ) {
00493     setFocusPoint( clickedPoint() );
00494     if ( Options::useAltAz() ) 
00495         focusPoint()->EquatorialToHorizontal( data->LST, data->geo()->lat() );
00496 
00497     //clear the planet trail of old focusObject, if it was temporary
00498     if ( focusObject() && focusObject()->isSolarSystem() && data->temporaryTrail ) {
00499         ((KSPlanetBase*)focusObject())->clearTrail();
00500         data->temporaryTrail = false;
00501     }
00502 
00503 //If the requested object is below the opaque horizon, issue a warning message
00504 //(unless user is already pointed below the horizon)
00505     if ( Options::useAltAz() && Options::showGround() &&
00506             focus()->alt()->Degrees() > -1.0 && focusPoint()->alt()->Degrees() < -1.0 ) {
00507 
00508         QString caption = i18n( "Requested Position Below Horizon" );
00509         QString message = i18n( "The requested position is below the horizon.\nWould you like to go there anyway?" );
00510         if ( KMessageBox::warningYesNo( this, message, caption,
00511                 i18n("Go Anyway"), i18n("Keep Position"), "dag_focus_below_horiz" )==KMessageBox::No ) {
00512             setClickedObject( NULL );
00513             setFocusObject( NULL );
00514             Options::setIsTracking( false );
00515 
00516             return;
00517         }
00518     }
00519 
00520 //set FocusObject before slewing.  Otherwise, KStarsData::updateTime() can reset
00521 //destination to previous object...
00522     setFocusObject( ClickedObject );
00523     Options::setIsTracking( true );
00524     if ( ksw ) {
00525       ksw->actionCollection()->action("track_object")->setIconSet( BarIcon( "encrypted" ) );
00526       ksw->toolBar( "mainToolBar" )->setButtonIconSet( 4, BarIcon( "encrypted" ) );
00527       ksw->actionCollection()->action("track_object")->setText( i18n( "Stop &Tracking" ) );
00528     }
00529 
00530     //If focusObject is a SS body and doesn't already have a trail, set the temporaryTrail
00531     if ( focusObject() && focusObject()->isSolarSystem()
00532          && Options::useAutoTrail()
00533             && ! ((KSPlanetBase*)focusObject())->hasTrail() ) {
00534         ((KSPlanetBase*)focusObject())->addToTrail();
00535         data->temporaryTrail = true;
00536     }
00537 
00538     //update the destination to the selected coordinates
00539     if ( Options::useAltAz() ) { 
00540         if ( Options::useRefraction() )
00541             setDestinationAltAz( refract( focusPoint()->alt(), true ).Degrees(), focusPoint()->az()->Degrees() );
00542         else
00543             setDestinationAltAz( focusPoint()->alt()->Degrees(), focusPoint()->az()->Degrees() );
00544     } else {
00545         setDestination( focusPoint() );
00546     }
00547 
00548     focusPoint()->EquatorialToHorizontal( data->LST, data->geo()->lat() );
00549 
00550     //display coordinates in statusBar
00551     if ( ksw ) {
00552         QString sX = focusPoint()->az()->toDMSString();
00553         QString sY = focusPoint()->alt()->toDMSString(true);
00554         if ( Options::useAltAz() && Options::useRefraction() )
00555             sY = refract( focusPoint()->alt(), true ).toDMSString(true);
00556         QString s = sX + ",  " + sY;
00557         ksw->statusBar()->changeItem( s, 1 );
00558         s = focusPoint()->ra()->toHMSString() + ",  " + focusPoint()->dec()->toDMSString(true);
00559         ksw->statusBar()->changeItem( s, 2 );
00560     }
00561     
00562     showFocusCoords(); //update FocusBox
00563 }
00564 
00565 void SkyMap::slotDSS( void ) {
00566     QString URLprefix( "http://archive.stsci.edu/cgi-bin/dss_search?v=1" );
00567     QString URLsuffix( "&e=J2000&h=15.0&w=15.0&f=gif&c=none&fov=NONE" );
00568     dms ra(0.0), dec(0.0);
00569     QString RAString, DecString;
00570     char decsgn;
00571 
00572     //ra and dec must be the coordinates at J2000.  If we clicked on an object, just use the object's ra0, dec0 coords
00573     //if we clicked on empty sky, we need to precess to J2000.
00574     if ( clickedObject() ) {
00575         ra.setH( clickedObject()->ra0()->Hours() );
00576         dec.setD( clickedObject()->dec0()->Degrees() );
00577     } else {
00578         //move present coords temporarily to ra0,dec0 (needed for precessToAnyEpoch)
00579         clickedPoint()->setRA0( clickedPoint()->ra()->Hours() );
00580         clickedPoint()->setDec0( clickedPoint()->dec()->Degrees() );
00581         clickedPoint()->precessFromAnyEpoch( data->ut().djd(), J2000 );
00582         ra.setH( clickedPoint()->ra()->Hours() );
00583         dec.setD( clickedPoint()->dec()->Degrees() );
00584 
00585         //restore coords from present epoch
00586         clickedPoint()->setRA( clickedPoint()->ra0()->Hours() );
00587         clickedPoint()->setDec( clickedPoint()->dec0()->Degrees() );
00588     }
00589 
00590     RAString = RAString.sprintf( "&r=%02d+%02d+%02d", ra.hour(), ra.minute(), ra.second() );
00591 
00592     decsgn = '+';
00593     if ( dec.Degrees() < 0.0 ) decsgn = '-';
00594     int dd = abs( dec.degree() );
00595     int dm = abs( dec.arcmin() );
00596     int ds = abs( dec.arcsec() );
00597     DecString = DecString.sprintf( "&d=%c%02d+%02d+%02d", decsgn, dd, dm, ds );
00598 
00599     //concat all the segments into the kview command line:
00600     KURL url (URLprefix + RAString + DecString + URLsuffix);
00601     
00602     QString message = i18n( "Digitized Sky Survey image provided by the Space Telescope Science Institute." );
00603     new ImageViewer (&url, message, this);
00604 }
00605 
00606 void SkyMap::slotDSS2( void ) {
00607     QString URLprefix( "http://archive.stsci.edu/cgi-bin/dss_search?v=2r" );
00608     QString URLsuffix( "&e=J2000&h=15.0&w=15.0&f=gif&c=none&fov=NONE" );
00609     dms ra(0.0), dec(0.0);
00610     QString RAString, DecString;
00611     char decsgn;
00612 
00613     //ra and dec must be the coordinates at J2000.  If we clicked on an object, just use the object's ra0, dec0 coords
00614     //if we clicked on empty sky, we need to precess to J2000.
00615     if ( clickedObject() ) {
00616         ra.setH( clickedObject()->ra0()->Hours() );
00617         dec.setD( clickedObject()->dec0()->Degrees() );
00618     } else {
00619         //move present coords temporarily to ra0,dec0 (needed for precessToAnyEpoch)
00620         clickedPoint()->setRA0( clickedPoint()->ra()->Hours() );
00621         clickedPoint()->setDec0( clickedPoint()->dec()->Degrees() );
00622         clickedPoint()->precessFromAnyEpoch( data->ut().djd(), J2000 );
00623         ra.setH( clickedPoint()->ra()->Hours() );
00624         dec.setD( clickedPoint()->dec()->Degrees() );
00625 
00626         //restore coords from present epoch
00627         clickedPoint()->setRA( clickedPoint()->ra0()->Hours() );
00628         clickedPoint()->setDec( clickedPoint()->dec0()->Degrees() );
00629     }
00630 
00631     RAString = RAString.sprintf( "&r=%02d+%02d+%02d", ra.hour(), ra.minute(), ra.second() );
00632 
00633     decsgn = '+';
00634     if ( dec.Degrees() < 0.0 ) decsgn = '-';
00635     int dd = abs( dec.degree() );
00636     int dm = abs( dec.arcmin() );
00637     int ds = abs( dec.arcsec() );
00638 
00639     DecString = DecString.sprintf( "&d=%c%02d+%02d+%02d", decsgn, dd, dm, ds );
00640 
00641     //concat all the segments into the kview command line:
00642     KURL url (URLprefix + RAString + DecString + URLsuffix);
00643     
00644     QString message = i18n( "Digitized Sky Survey image provided by the Space Telescope Science Institute." );
00645     new ImageViewer (&url, message, this);
00646 }
00647 
00648 void SkyMap::slotInfo( int id ) {
00649     QStringList::Iterator it = clickedObject()->InfoList.at(id-200);
00650     QString sURL = (*it);
00651     KURL url ( sURL );
00652     if (!url.isEmpty())
00653         kapp->invokeBrowser(sURL);
00654 }
00655 
00656 void SkyMap::slotBeginAngularDistance(void) {
00657     setPreviousClickedPoint( mousePoint() );
00658     angularDistanceMode = true;
00659     beginRulerPoint = getXY( previousClickedPoint(), Options::useAltAz(), Options::useRefraction() );
00660     endRulerPoint =  QPoint( beginRulerPoint.x(),beginRulerPoint.y() );
00661 }
00662 
00663 void SkyMap::slotEndAngularDistance(void) {
00664     dms angularDistance;
00665     if(angularDistanceMode) {
00666         if ( SkyObject *so = objectNearest( mousePoint() ) ) {
00667             angularDistance = so->angularDistanceTo( previousClickedPoint() );
00668             ksw->statusBar()->changeItem( so->translatedLongName() + 
00669                     "     " +
00670                     i18n("Angular distance: " ) +
00671                     angularDistance.toDMSString(), 0 );
00672         } else {
00673             angularDistance = mousePoint()->angularDistanceTo( previousClickedPoint() );
00674             ksw->statusBar()->changeItem( i18n("Angular distance: " ) +
00675                 angularDistance.toDMSString(), 0 );
00676         }
00677         angularDistanceMode=false;
00678     }
00679 }
00680 
00681 void SkyMap::slotCancelAngularDistance(void) {
00682     angularDistanceMode=false;
00683 }
00684 
00685 void SkyMap::slotImage( int id ) {
00686     QStringList::Iterator it = clickedObject()->ImageList.at(id-100);
00687     QStringList::Iterator it2 = clickedObject()->ImageTitle.at(id-100);
00688     QString sURL = (*it);
00689     QString message = (*it2);
00690     KURL url ( sURL );
00691     if (!url.isEmpty())
00692         new ImageViewer (&url, clickedObject()->messageFromTitle(message), this);
00693 }
00694 
00695 bool SkyMap::isObjectLabeled( SkyObject *object ) {
00696     for ( SkyObject *o = data->ObjLabelList.first(); o; o = data->ObjLabelList.next() ) {
00697         if ( o == object ) return true;
00698     }
00699 
00700     return false;
00701 }
00702 
00703 void SkyMap::slotRemoveObjectLabel( void ) {
00704     for ( SkyObject *o = data->ObjLabelList.first(); o; o = data->ObjLabelList.next() ) {
00705         if ( o == clickedObject() ) {
00706             //remove object from list
00707             data->ObjLabelList.remove();
00708             break;
00709         }
00710     }
00711 
00712     forceUpdate();
00713 }
00714 
00715 void SkyMap::slotAddObjectLabel( void ) {
00716     data->ObjLabelList.append( clickedObject() );
00717     //Since we just added a permanent label, we don't want it to fade away!
00718     if ( transientObject() == clickedObject() ) setTransientObject( NULL );
00719     forceUpdate();
00720 }
00721 
00722 void SkyMap::slotRemovePlanetTrail( void ) {
00723     //probably don't need this if-statement, but just to be sure...
00724     if ( clickedObject() && clickedObject()->isSolarSystem() ) {
00725         ((KSPlanetBase*)clickedObject())->clearTrail();
00726         forceUpdate();
00727     }
00728 }
00729 
00730 void SkyMap::slotAddPlanetTrail( void ) {
00731     //probably don't need this if-statement, but just to be sure...
00732     if ( clickedObject() && clickedObject()->isSolarSystem() ) {
00733         ((KSPlanetBase*)clickedObject())->addToTrail();
00734         forceUpdate();
00735     }
00736 }
00737 
00738 void SkyMap::slotDetail( void ) {
00739 // check if object is selected
00740     if ( !clickedObject() ) {
00741         KMessageBox::sorry( this, i18n("No object selected."), i18n("Object Details") );
00742         return;
00743     }
00744     DetailDialog detail( clickedObject(), data->ut(), data->geo(), ksw );
00745     detail.exec();
00746 }
00747 
00748 void SkyMap::slotClockSlewing() {
00749 //If the current timescale exceeds slewTimeScale, set clockSlewing=true, and stop the clock.
00750     if ( fabs( data->clock()->scale() ) > Options::slewTimeScale() ) {
00751         if ( ! clockSlewing ) {
00752             clockSlewing = true;
00753             data->clock()->setManualMode( true );
00754 
00755             // don't change automatically the DST status
00756             if ( ksw ) ksw->updateTime( false );
00757         }
00758     } else {
00759         if ( clockSlewing ) {
00760             clockSlewing = false;
00761             data->clock()->setManualMode( false );
00762 
00763             // don't change automatically the DST status
00764             if ( ksw ) ksw->updateTime( false );
00765         }
00766     }
00767 }
00768 
00769 void SkyMap::setFocus( SkyPoint *p ) {
00770     setFocus( p->ra()->Hours(), p->dec()->Degrees() );
00771 }
00772 
00773 void SkyMap::setFocus( const dms &ra, const dms &dec ) {
00774     setFocus( ra.Hours(), dec.Degrees() );
00775 }
00776 
00777 void SkyMap::setFocus( double ra, double dec ) {
00778     Focus.set( ra, dec );
00779     focus()->EquatorialToHorizontal( data->LST, data->geo()->lat() );
00780 }
00781 
00782 void SkyMap::setFocusAltAz( const dms &alt, const dms &az) {
00783     setFocusAltAz( alt.Degrees(), az.Degrees() );
00784 }
00785 
00786 void SkyMap::setFocusAltAz(double alt, double az) {
00787     focus()->setAlt(alt);
00788     focus()->setAz(az);
00789     focus()->HorizontalToEquatorial( data->LST, data->geo()->lat() );
00790     slewing = false;
00791 
00792     oldfocus()->set( focus()->ra(), focus()->dec() );
00793     oldfocus()->setAz( focus()->az()->Degrees() );
00794     oldfocus()->setAlt( focus()->alt()->Degrees() );
00795 
00796     double dHA = data->LST->Hours() - focus()->ra()->Hours();
00797     while ( dHA < 0.0 ) dHA += 24.0;
00798     data->HourAngle->setH( dHA );
00799 
00800     forceUpdate(); //need a total update, or slewing with the arrow keys doesn't work.
00801 }
00802 
00803 void SkyMap::setDestination( SkyPoint *p ) {
00804     Destination.set( p->ra(), p->dec() );
00805     destination()->EquatorialToHorizontal( data->LST, data->geo()->lat() );
00806     emit destinationChanged();
00807 }
00808 
00809 void SkyMap::setDestination( const dms &ra, const dms &dec ) {
00810     Destination.set( ra, dec );
00811     destination()->EquatorialToHorizontal( data->LST, data->geo()->lat() );
00812     emit destinationChanged();
00813 }
00814 
00815 void SkyMap::setDestination( double ra, double dec ) {
00816     Destination.set( ra, dec );
00817     destination()->EquatorialToHorizontal( data->LST, data->geo()->lat() );
00818     emit destinationChanged();
00819 }
00820 
00821 void SkyMap::setDestinationAltAz( const dms &alt, const dms &az) {
00822     destination()->setAlt(alt);
00823     destination()->setAz(az);
00824     destination()->HorizontalToEquatorial( data->LST, data->geo()->lat() );
00825     emit destinationChanged();
00826 }
00827 
00828 void SkyMap::setDestinationAltAz(double alt, double az) {
00829     destination()->setAlt(alt);
00830     destination()->setAz(az);
00831     destination()->HorizontalToEquatorial( data->LST, data->geo()->lat() );
00832     emit destinationChanged();
00833 }
00834 
00835 void SkyMap::updateFocus() {
00836     if ( Options::isTracking() && focusObject() != NULL ) {
00837         if ( Options::useAltAz() ) {
00838             //Tracking any object in Alt/Az mode requires focus updates
00839             double dAlt = focusObject()->alt()->Degrees();
00840             if ( Options::useRefraction() ) 
00841                 dAlt = refract( focusObject()->alt(), true ).Degrees();
00842             setFocusAltAz( dAlt, focusObject()->az()->Degrees() );
00843             focus()->HorizontalToEquatorial( data->LST, data->geo()->lat() );
00844             setDestination( focus() );
00845         } else {
00846             //Tracking in equatorial coords
00847             setFocus( focusObject() );
00848             focus()->EquatorialToHorizontal( data->LST, data->geo()->lat() );
00849             setDestination( focus() );
00850         }
00851     } else if ( Options::isTracking() && focusPoint() != NULL ) {
00852         if ( Options::useAltAz() ) {
00853             //Tracking on empty sky in Alt/Az mode
00854             setFocus( focusPoint() );
00855             focus()->EquatorialToHorizontal( data->LST, data->geo()->lat() );
00856             setDestination( focus() );
00857         }
00858     } else if ( ! slewing ) {
00859         //Not tracking and not slewing, let sky drift by
00860         if ( Options::useAltAz() ) {
00861             focus()->setAlt( destination()->alt()->Degrees() );
00862             focus()->setAz( destination()->az()->Degrees() );
00863             focus()->HorizontalToEquatorial( data->LST, data->geo()->lat() );
00864             //destination()->HorizontalToEquatorial( data->LST, data->geo()->lat() );
00865         } else {
00866             focus()->setRA( data->LST->Hours() - data->HourAngle->Hours() );
00867             setDestination( focus() );
00868             focus()->EquatorialToHorizontal( data->LST, data->geo()->lat() );
00869             destination()->EquatorialToHorizontal( data->LST, data->geo()->lat() );
00870         }
00871     }
00872 
00873     //Update the Hour Angle
00874     data->setHourAngle( data->LST->Hours() - focus()->ra()->Hours() );
00875 
00876     setOldFocus( focus() );
00877     oldfocus()->EquatorialToHorizontal( data->LST, data->geo()->lat() );
00878 }
00879 
00880 void SkyMap::slewFocus( void ) {
00881     double dX, dY, fX, fY, r;
00882     double step = 1.0;
00883     SkyPoint newFocus;
00884 
00885 //Don't slew if the mouse button is pressed
00886 //Also, no animated slews if the Manual Clock is active
00887 //08/2002: added possibility for one-time skipping of slew with snapNextFocus
00888     if ( !mouseButtonDown ) {
00889         bool goSlew = ( Options::useAnimatedSlewing() &&
00890             ! data->snapNextFocus() ) &&
00891             !( data->clock()->isManualMode() && data->clock()->isActive() );
00892         if ( goSlew  ) {
00893             if ( Options::useAltAz() ) {
00894                 dX = destination()->az()->Degrees() - focus()->az()->Degrees();
00895                 dY = destination()->alt()->Degrees() - focus()->alt()->Degrees();
00896             } else {
00897                 dX = destination()->ra()->Degrees() - focus()->ra()->Degrees();
00898                 dY = destination()->dec()->Degrees() - focus()->dec()->Degrees();
00899             }
00900 
00901             //switch directions to go the short way around the celestial sphere, if necessary.
00902             if ( dX < -180.0 ) dX = 360.0 + dX;
00903             else if ( dX > 180.0 ) dX = -360.0 + dX;
00904 
00905             r = sqrt( dX*dX + dY*dY );
00906 
00907             while ( r > step ) {
00908                 fX = dX / r;
00909                 fY = dY / r;
00910 
00911                 if ( Options::useAltAz() ) {
00912                     focus()->setAlt( focus()->alt()->Degrees() + fY*step );
00913                     focus()->setAz( dms( focus()->az()->Degrees() + fX*step ).reduce() );
00914                     focus()->HorizontalToEquatorial( data->LST, data->geo()->lat() );
00915                 } else {
00916                     fX = fX/15.; //convert RA degrees to hours
00917                     newFocus.set( focus()->ra()->Hours() + fX*step, focus()->dec()->Degrees() + fY*step );
00918                     setFocus( &newFocus );
00919                     focus()->EquatorialToHorizontal( data->LST, data->geo()->lat() );
00920                 }
00921 
00922                 slewing = true;
00923                 //since we are slewing, fade out the transient label
00924                 if ( transientObject() && ! TransientTimer.isActive() ) 
00925                     fadeTransientLabel();
00926                 
00927                 forceUpdate();
00928                 kapp->processEvents(10); //keep up with other stuff
00929 
00930                 if ( Options::useAltAz() ) {
00931                     dX = destination()->az()->Degrees() - focus()->az()->Degrees();
00932                     dY = destination()->alt()->Degrees() - focus()->alt()->Degrees();
00933                 } else {
00934                     dX = destination()->ra()->Degrees() - focus()->ra()->Degrees();
00935                     dY = destination()->dec()->Degrees() - focus()->dec()->Degrees();
00936                 }
00937 
00938                 //switch directions to go the short way around the celestial sphere, if necessary.
00939                 if ( dX < -180.0 ) dX = 360.0 + dX;
00940                 else if ( dX > 180.0 ) dX = -360.0 + dX;
00941 
00942                 r = sqrt( dX*dX + dY*dY );
00943             }
00944         }
00945 
00946         //Either useAnimatedSlewing==false, or we have slewed, and are within one step of destination
00947         //set focus=destination.
00948         if ( Options::useAltAz() ) {
00949             setFocusAltAz( destination()->alt()->Degrees(), destination()->az()->Degrees() );
00950             focus()->HorizontalToEquatorial( data->LST, data->geo()->lat() );
00951         } else {
00952             setFocus( destination() );
00953             focus()->EquatorialToHorizontal( data->LST, data->geo()->lat() );
00954         }
00955         
00956         data->HourAngle->setH( data->LST->Hours() - focus()->ra()->Hours() );
00957         slewing = false;
00958 
00959         //Turn off snapNextFocus, we only want it to happen once
00960         if ( data->snapNextFocus() ) {
00961             data->setSnapNextFocus(false);
00962         }
00963 
00964         //Start the HoverTimer. if the user leaves the mouse in place after a slew,
00965         //we want to attach a label to the nearest object.
00966         if ( Options::useHoverLabel() )
00967             HoverTimer.start( HOVER_INTERVAL, true );
00968         
00969         forceUpdate();
00970     }
00971 }
00972 
00973 void SkyMap::invokeKey( int key ) {
00974     QKeyEvent *e = new QKeyEvent( QEvent::KeyPress, key, 0, 0 );
00975     keyPressEvent( e );
00976     delete e;
00977 }
00978 
00979 double SkyMap::findPA( SkyObject *o, int x, int y, double scale ) {
00980     //Find position angle of North using a test point displaced to the north
00981     //displace by 100/zoomFactor radians (so distance is always 100 pixels)
00982     //this is 5730/zoomFactor degrees
00983     double newDec = o->dec()->Degrees() + 5730.0/Options::zoomFactor();
00984     if ( newDec > 90.0 ) newDec = 90.0;
00985     SkyPoint test( o->ra()->Hours(), newDec );
00986     if ( Options::useAltAz() ) test.EquatorialToHorizontal( data->LST, data->geo()->lat() );
00987     QPoint t = getXY( &test, Options::useAltAz(), Options::useRefraction(), scale );
00988     double dx = double( t.x() - x );  
00989     double dy = double( y - t.y() );  //backwards because QWidget Y-axis increases to the bottom
00990     double north;
00991     if ( dy ) {
00992         north = atan( dx/dy )*180.0/dms::PI;
00993         //resolve atan ambiguity:
00994         if ( dy < 0.0 ) north += 180.0;
00995         if ( north >= 360.0 ) north -= 360.;
00996     } else {
00997         north = 90.0;
00998         if ( dx > 0 ) north = -90.0;
00999     }
01000 
01001     return ( north + o->pa() );
01002 }
01003 
01004 QPoint SkyMap::getXY( SkyPoint *o, bool Horiz, bool doRefraction, double scale ) {
01005     QPoint p;
01006 
01007     double Y, dX;
01008     double sindX, cosdX, sinY, cosY, sinY0, cosY0;
01009 
01010     int Width = int( width() * scale );
01011     int Height = int( height() * scale );
01012 
01013     double pscale = Options::zoomFactor() * scale;
01014 
01015     if ( Horiz ) {
01016         if ( doRefraction ) Y = refract( o->alt(), true ).radians(); //account for atmospheric refraction
01017         else Y = o->alt()->radians();
01018 
01019         if ( focus()->az()->Degrees() > 270.0 && o->az()->Degrees() < 90.0 ) {
01020             dX = 2*dms::PI + focus()->az()->radians() - o->az()->radians();
01021         } else {
01022             dX = focus()->az()->radians() - o->az()->radians();
01023         }
01024 
01025         focus()->alt()->SinCos( sinY0, cosY0 );
01026 
01027   } else {
01028         if (focus()->ra()->Hours() > 18.0 && o->ra()->Hours() < 6.0) {
01029             dX = 2*dms::PI + o->ra()->radians() - focus()->ra()->radians();
01030         } else {
01031             dX = o->ra()->radians() - focus()->ra()->radians();
01032         }
01033     Y = o->dec()->radians();
01034         focus()->dec()->SinCos( sinY0, cosY0 );
01035   }
01036 
01037     //Convert dX, Y coords to screen pixel coords.
01038     #if ( __GLIBC__ >= 2 && __GLIBC_MINOR__ >=1 ) && !defined(__UCLIBC__)
01039     //GNU version
01040     sincos( dX, &sindX, &cosdX );
01041     sincos( Y, &sinY, &cosY );
01042     #else
01043     //ANSI version
01044     sindX = sin(dX);
01045     cosdX = cos(dX);
01046     sinY  = sin(Y);
01047     cosY  = cos(Y);
01048     #endif
01049 
01050     double c = sinY0*sinY + cosY0*cosY*cosdX;
01051 
01052     if ( c < 0.0 ) { //Object is on "back side" of the celestial sphere; don't plot it.
01053         p.setX( -10000000 );
01054         p.setY( -10000000 );
01055         return p;
01056     }
01057 
01058     double k = sqrt( 2.0/( 1 + c ) );
01059 
01060     p.setX( int( 0.5*Width  - pscale*k*cosY*sindX ) );
01061     p.setY( int( 0.5*Height - pscale*k*( cosY0*sinY - sinY0*cosY*cosdX ) ) );
01062 
01063     return p;
01064 }
01065 
01066 SkyPoint SkyMap::dXdYToRaDec( double dx, double dy, bool useAltAz, dms *LST, const dms *lat, bool doRefract ) {
01067     //Determine RA and Dec of a point, given (dx, dy): it's pixel
01068     //coordinates in the SkyMap with the center of the map as the origin.
01069 
01070     SkyPoint result;
01071     double sinDec, cosDec, sinDec0, cosDec0, sinc, cosc, sinlat, coslat;
01072     double xx, yy;
01073 
01074     double r  = sqrt( dx*dx + dy*dy );
01075     dms centerAngle;
01076     centerAngle.setRadians( 2.0*asin(0.5*r) );
01077 
01078     focus()->dec()->SinCos( sinDec0, cosDec0 );
01079     centerAngle.SinCos( sinc, cosc );
01080 
01081     if ( useAltAz ) {
01082         dms HA;
01083         dms Dec, alt, az, alt0, az0;
01084         double A;
01085         double sinAlt, cosAlt, sinAlt0, cosAlt0, sinAz, cosAz;
01086 //      double HA0 = LST - focus.ra();
01087         az0 = focus()->az()->Degrees();
01088         alt0 = focus()->alt()->Degrees();
01089         alt0.SinCos( sinAlt0, cosAlt0 );
01090 
01091         dx = -dx; //Flip East-west (Az goes in opposite direction of RA)
01092         yy = dx*sinc;
01093         xx = r*cosAlt0*cosc - dy*sinAlt0*sinc;
01094 
01095         A = atan( yy/xx );
01096         //resolve ambiguity of atan():
01097         if ( xx<0 ) A = A + dms::PI;
01098 //      if ( xx>0 && yy<0 ) A = A + 2.0*dms::PI;
01099 
01100         dms deltaAz;
01101         deltaAz.setRadians( A );
01102         az = focus()->az()->Degrees() + deltaAz.Degrees();
01103         alt.setRadians( asin( cosc*sinAlt0 + ( dy*sinc*cosAlt0 )/r ) );
01104 
01105         if ( doRefract ) alt.setD( refract( &alt, false ).Degrees() );  //find true altitude from apparent altitude
01106 
01107         az.SinCos( sinAz, cosAz );
01108         alt.SinCos( sinAlt, cosAlt );
01109         lat->SinCos( sinlat, coslat );
01110 
01111         Dec.setRadians( asin( sinAlt*sinlat + cosAlt*coslat*cosAz ) );
01112         Dec.SinCos( sinDec, cosDec );
01113 
01114         HA.setRadians( acos( ( sinAlt - sinlat*sinDec )/( coslat*cosDec ) ) );
01115         if ( sinAz > 0.0 ) HA.setH( 24.0 - HA.Hours() );
01116 
01117         result.setRA( LST->Hours() - HA.Hours() );
01118         result.setRA( result.ra()->reduce() );
01119         result.setDec( Dec.Degrees() );
01120 
01121         return result;
01122 
01123   } else {
01124         yy = dx*sinc;
01125         xx = r*cosDec0*cosc - dy*sinDec0*sinc;
01126 
01127         double RARad = ( atan( yy / xx ) );
01128         //resolve ambiguity of atan():
01129         if ( xx<0 ) RARad = RARad + dms::PI;
01130 //      if ( xx>0 && yy<0 ) RARad = RARad + 2.0*dms::PI;
01131 
01132         dms deltaRA, Dec;
01133         deltaRA.setRadians( RARad );
01134         Dec.setRadians( asin( cosc*sinDec0 + (dy*sinc*cosDec0)/r ) );
01135 
01136         result.setRA( focus()->ra()->Hours() + deltaRA.Hours() );
01137         result.setRA( result.ra()->reduce() );
01138         result.setDec( Dec.Degrees() );
01139 
01140         return result;
01141     }
01142 }
01143 
01144 dms SkyMap::refract( const dms *alt, bool findApparent ) {
01145     if ( alt->Degrees() <= -2.000 ) return dms( alt->Degrees() );
01146 
01147     int index = int( ( alt->Degrees() + 2.0 )*2. );  //RefractCorr arrays start at alt=-2.0 degrees.
01148     dms result;
01149 
01150     //Failsafe: if the index is out of range, return the original angle
01151     if ( index < 0 || index > 183 ) {
01152         return dms( alt->Degrees() );
01153     }
01154 
01155     if ( findApparent ) {
01156         result.setD( alt->Degrees() + RefractCorr1[index] );
01157     } else {
01158         result.setD( alt->Degrees() + RefractCorr2[index] );
01159     }
01160 
01161     return result;
01162 }
01163 
01164 //---------------------------------------------------------------------------
01165 
01166 
01167 // force a new calculation of the skymap (used instead of update(), which may skip the redraw)
01168 // if now=true, SkyMap::paintEvent() is run immediately, rather than being added to the event queue
01169 // also, determine new coordinates of mouse cursor.
01170 void SkyMap::forceUpdate( bool now )
01171 {
01172     QPoint mp( mapFromGlobal( QCursor::pos() ) );
01173     double dx = ( 0.5*width()  - mp.x() )/Options::zoomFactor();
01174     double dy = ( 0.5*height() - mp.y() )/Options::zoomFactor();
01175 
01176     if (! unusablePoint (dx, dy)) {
01177         //determine RA, Dec of mouse pointer
01178         setMousePoint( dXdYToRaDec( dx, dy, Options::useAltAz(), data->LST, data->geo()->lat(), Options::useRefraction() ) );
01179     }
01180 
01181     computeSkymap = true;
01182     if ( now ) repaint();
01183     else update();
01184 }
01185 
01186 float SkyMap::fov() {
01187     if ( width() >= height() ) 
01188         return 28.65*width()/Options::zoomFactor();
01189     else
01190         return 28.65*height()/Options::zoomFactor();
01191 }
01192 
01193 bool SkyMap::checkVisibility( SkyPoint *p, float FOV, double XMax ) {
01194     double dX, dY;
01195     bool useAltAz = Options::useAltAz();
01196 
01197     //Skip objects below the horizon if:
01198     // + using Horizontal coords,
01199     // + the ground is drawn,
01200     // + and either of the following is true:
01201     //   - focus is above the horizon
01202     //   - field of view is larger than 50 degrees
01203     if ( useAltAz && Options::showGround() && p->alt()->Degrees() < -2.0 
01204                 && ( focus()->alt()->Degrees() > 0. || FOV > 50. ) ) return false;
01205 
01206     if ( useAltAz ) {
01207         dY = fabs( p->alt()->Degrees() - focus()->alt()->Degrees() );
01208     } else {
01209         dY = fabs( p->dec()->Degrees() - focus()->dec()->Degrees() );
01210     }
01211     if ( isPoleVisible ) dY *= 0.75; //increase effective FOV when pole visible.
01212     if ( dY > FOV ) return false;
01213     if ( isPoleVisible ) return true;
01214 
01215     if ( useAltAz ) {
01216         dX = fabs( p->az()->Degrees() - focus()->az()->Degrees() );
01217     } else {
01218         dX = fabs( p->ra()->Degrees() - focus()->ra()->Degrees() );
01219     }
01220     if ( dX > 180.0 ) dX = 360.0 - dX; // take shorter distance around sky
01221 
01222     if ( dX < XMax ) {
01223         return true;
01224     } else {
01225         return false;
01226     }
01227 }
01228 
01229 bool SkyMap::unusablePoint (double dx, double dy)
01230 {
01231     if (dx >= 1.41 || dx <= -1.41 || dy >= 1.41 || dy <= -1.41)
01232         return true;
01233     else
01234         return false;
01235 }
01236 
01237 void SkyMap::setZoomMouseCursor()
01238 {
01239     mouseMoveCursor = false;    // no mousemove cursor
01240     
01241     QPainter p;
01242     QPixmap cursorPix (32, 32); // size 32x32 (this size is compatible to all systems)
01243 // the center of the pixmap
01244     int mx = cursorPix. width() / 2;
01245     int my = cursorPix. height() / 2;
01246 
01247     cursorPix.fill (white);  // white background
01248     p.begin (&cursorPix);
01249     p.setPen (QPen (black, 2)); // black lines
01250 
01251     p.drawEllipse( mx - 7, my - 7, 14, 14 );
01252     p.drawLine( mx + 5, my + 5, mx + 11, my + 11 );
01253     p.end();
01254 
01255 // create a mask to make parts of the pixmap invisible
01256     QBitmap mask (32, 32);
01257     mask.fill (color0); // all is invisible
01258 
01259     p.begin (&mask);
01260 // paint over the parts which should be visible
01261     p.setPen (QPen (color1, 3));
01262     p.drawEllipse( mx - 7, my - 7, 14, 14 );
01263     p.drawLine( mx + 5, my + 5, mx + 12, my + 12 );
01264     p.end();
01265     
01266     cursorPix.setMask (mask);   // set the mask
01267     QCursor cursor (cursorPix);
01268     setCursor (cursor);
01269 }
01270 
01271 void SkyMap::setDefaultMouseCursor()
01272 {
01273     mouseMoveCursor = false;    // no mousemove cursor
01274     
01275     QPainter p;
01276     QPixmap cursorPix (32, 32); // size 32x32 (this size is compatible to all systems)
01277 // the center of the pixmap
01278     int mx = cursorPix. width() / 2;
01279     int my = cursorPix. height() / 2;
01280 
01281     cursorPix.fill (white);  // white background
01282     p.begin (&cursorPix);
01283     p.setPen (QPen (black, 2)); // black lines
01284 // 1. diagonal
01285     p.drawLine (mx - 2, my - 2, mx - 8, mx - 8);
01286     p.drawLine (mx + 2, my + 2, mx + 8, mx + 8);
01287 // 2. diagonal
01288     p.drawLine (mx - 2, my + 2, mx - 8, mx + 8);
01289     p.drawLine (mx + 2, my - 2, mx + 8, mx - 8);
01290     p.end();
01291 
01292 // create a mask to make parts of the pixmap invisible
01293     QBitmap mask (32, 32);
01294     mask.fill (color0); // all is invisible
01295 
01296     p.begin (&mask);
01297 // paint over the parts which should be visible
01298     p.setPen (QPen (color1, 3));
01299 // 1. diagonal
01300     p.drawLine (mx - 2, my - 2, mx - 8, mx - 8);
01301     p.drawLine (mx + 2, my + 2, mx + 8, mx + 8);
01302 // 2. diagonal
01303     p.drawLine (mx - 2, my + 2, mx - 8, mx + 8);
01304     p.drawLine (mx + 2, my - 2, mx + 8, mx - 8);
01305     p.end();
01306 
01307     cursorPix.setMask (mask);   // set the mask
01308     QCursor cursor (cursorPix);
01309     setCursor (cursor);
01310 }
01311 
01312 void SkyMap::setMouseMoveCursor()
01313 {
01314     if (mouseButtonDown)
01315     {
01316         setCursor (9);  // cursor shape defined in qt
01317         mouseMoveCursor = true;
01318     }
01319 }
01320 
01321 void SkyMap::addLink( void ) {
01322     AddLinkDialog adialog( this, clickedObject()->name() );
01323     QString entry;
01324   QFile file;
01325 
01326     if ( adialog.exec()==QDialog::Accepted ) {
01327         if ( adialog.isImageLink() ) {
01328             //Add link to object's ImageList, and descriptive text to its ImageTitle list
01329             clickedObject()->ImageList.append( adialog.url() );
01330             clickedObject()->ImageTitle.append( adialog.desc() );
01331 
01332             //Also, update the user's custom image links database
01333             //check for user's image-links database.  If it doesn't exist, create it.
01334             file.setName( locateLocal( "appdata", "image_url.dat" ) ); //determine filename in local user KDE directory tree.
01335 
01336             if ( !file.open( IO_ReadWrite | IO_Append ) ) {
01337                 QString message = i18n( "Custom image-links file could not be opened.\nLink cannot be recorded for future sessions." );
01338                 KMessageBox::sorry( 0, message, i18n( "Could Not Open File" ) );
01339                 return;
01340             } else {
01341                 entry = clickedObject()->name() + ":" + adialog.desc() + ":" + adialog.url();
01342                 QTextStream stream( &file );
01343                 stream << entry << endl;
01344                 file.close();
01345                 emit linkAdded();
01346             }
01347         } else {
01348             clickedObject()->InfoList.append( adialog.url() );
01349             clickedObject()->InfoTitle.append( adialog.desc() );
01350 
01351             //check for user's image-links database.  If it doesn't exist, create it.
01352             file.setName( locateLocal( "appdata", "info_url.dat" ) ); //determine filename in local user KDE directory tree.
01353 
01354             if ( !file.open( IO_ReadWrite | IO_Append ) ) {
01355                 QString message = i18n( "Custom information-links file could not be opened.\nLink cannot be recorded for future sessions." );                       KMessageBox::sorry( 0, message, i18n( "Could not Open File" ) );
01356                 return;
01357             } else {
01358                 entry = clickedObject()->name() + ":" + adialog.desc() + ":" + adialog.url();
01359                 QTextStream stream( &file );
01360                 stream << entry << endl;
01361                 file.close();
01362                 emit linkAdded();
01363             }
01364         }
01365     }
01366 }
01367 
01368 void SkyMap::updateAngleRuler() {
01369     if ( Options::useAltAz() ) PreviousClickedPoint.EquatorialToHorizontal( data->LST, data->geo()->lat() );
01370     beginRulerPoint = getXY( previousClickedPoint(), Options::useAltAz(), Options::useRefraction() );
01371 
01372 //  endRulerPoint =  QPoint(e->x(), e->y());
01373     endRulerPoint = mapFromGlobal( QCursor::pos() );
01374 }
01375 
01376 #include "skymap.moc"

kstars

Skip menu "kstars"
  • Main Page
  • Modules
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

API Reference

Skip menu "API Reference"
  • keduca
  • kstars
Generated for API Reference by doxygen 1.5.9
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal