/***************************************************************************
skyobject . cpp - K Desktop Planetarium
- - - - - - - - - - - - - - - - - - -
begin : Sun Feb 11 2001
copyright : ( C ) 2001 by Jason Harris
email : jharris @ 30 doradus . org
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
/***************************************************************************
* *
* 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 <iostream>
# include <kglobal.h>
# include <kstandarddirs.h>
# include <tqpoint.h>
# include <tqregexp.h>
# include <tqfile.h>
# include <tqtextstream.h>
# include "skyobject.h"
# include "starobject.h" //needed in saveUserLog()
# include "ksnumbers.h"
# include "dms.h"
# include "geolocation.h"
# include "kstarsdatetime.h"
TQString SkyObject : : emptyString = TQString ( " " ) ;
TQString SkyObject : : unnamedString = TQString ( i18n ( " unnamed " ) ) ;
TQString SkyObject : : unnamedObjectString = TQString ( i18n ( " unnamed object " ) ) ;
TQString SkyObject : : starString = TQString ( " star " ) ;
SkyObject : : SkyObject ( SkyObject & o ) : SkyPoint ( o ) {
setType ( o . type ( ) ) ;
Magnitude = o . mag ( ) ;
setName ( o . name ( ) ) ;
setName2 ( o . name2 ( ) ) ;
setLongName ( o . longname ( ) ) ;
ImageList = o . ImageList ;
ImageTitle = o . ImageTitle ;
InfoList = o . InfoList ;
InfoTitle = o . InfoTitle ;
}
SkyObject : : SkyObject ( int t , dms r , dms d , float m ,
TQString n , TQString n2 , TQString lname ) : SkyPoint ( r , d ) {
setType ( t ) ;
Magnitude = m ;
Name = 0 ;
setName ( n ) ;
Name2 = 0 ;
setName2 ( n2 ) ;
LongName = 0 ;
setLongName ( lname ) ;
}
SkyObject : : SkyObject ( int t , double r , double d , float m ,
TQString n , TQString n2 , TQString lname ) : SkyPoint ( r , d ) {
setType ( t ) ;
Magnitude = m ;
Name = 0 ;
setName ( n ) ;
Name2 = 0 ;
setName2 ( n2 ) ;
LongName = 0 ;
setLongName ( lname ) ;
}
SkyObject : : ~ SkyObject ( ) {
delete Name ;
delete Name2 ;
delete LongName ;
}
void SkyObject : : setLongName ( const TQString & longname ) {
delete LongName ;
if ( longname . isEmpty ( ) ) {
if ( hasName ( ) )
LongName = new TQString ( translatedName ( ) ) ;
else if ( hasName2 ( ) )
LongName = new TQString ( * Name2 ) ;
else
LongName = 0 ;
} else {
LongName = new TQString ( longname ) ;
}
}
TQTime SkyObject : : riseSetTime ( const KStarsDateTime & dt , const GeoLocation * geo , bool rst ) {
//this object does not rise or set; return an invalid time
if ( checkCircumpolar ( geo - > lat ( ) ) )
return TQTime ( 25 , 0 , 0 ) ;
//First of all, if the object is below the horizon at date/time dt, adjust the time
//to bring it above the horizon
KStarsDateTime dt2 = dt ;
SkyPoint p = recomputeCoords ( dt , geo ) ;
dms gegsls = ( geo - > GSTtoLST ( dt . gst ( ) ) ) ;
p . EquatorialToHorizontal ( & gegsls , geo - > lat ( ) ) ;
if ( p . alt ( ) - > Degrees ( ) < 0.0 ) {
if ( p . az ( ) - > Degrees ( ) < 180.0 ) { //object has not risen yet
dt2 = dt . addSecs ( 12. * 3600. ) ;
} else { //object has already set
dt2 = dt . addSecs ( - 12. * 3600. ) ;
}
}
return geo - > UTtoLT ( KStarsDateTime ( dt2 . date ( ) , riseSetTimeUT ( dt2 , geo , rst ) ) ) . time ( ) ;
}
TQTime SkyObject : : riseSetTimeUT ( const KStarsDateTime & dt , const GeoLocation * geo , bool riseT ) {
// First trial to calculate UT
TQTime UT = auxRiseSetTimeUT ( dt , geo , ra ( ) , dec ( ) , riseT ) ;
// We iterate once more using the calculated UT to compute again
// the ra and dec for that time and hence the rise/set time.
// Also, adjust the date by +/- 1 day, if necessary
KStarsDateTime dt0 = dt ;
dt0 . setTime ( UT ) ;
if ( riseT & & dt0 > dt ) {
dt0 = dt0 . addDays ( - 1 ) ;
} else if ( ! riseT & & dt0 < dt ) {
dt0 = dt0 . addDays ( 1 ) ;
}
SkyPoint sp = recomputeCoords ( dt0 , geo ) ;
UT = auxRiseSetTimeUT ( dt0 , geo , sp . ra ( ) , sp . dec ( ) , riseT ) ;
// We iterate a second time (For the Moon the second iteration changes
// aprox. 1.5 arcmin the coordinates).
dt0 . setTime ( UT ) ;
sp = recomputeCoords ( dt0 , geo ) ;
UT = auxRiseSetTimeUT ( dt0 , geo , sp . ra ( ) , sp . dec ( ) , riseT ) ;
return UT ;
}
dms SkyObject : : riseSetTimeLST ( const KStarsDateTime & dt , const GeoLocation * geo , bool riseT ) {
KStarsDateTime rst ( dt . date ( ) , riseSetTimeUT ( dt , geo , riseT ) ) ;
return geo - > GSTtoLST ( rst . gst ( ) ) ;
}
TQTime SkyObject : : auxRiseSetTimeUT ( const KStarsDateTime & dt , const GeoLocation * geo ,
const dms * righta , const dms * decl , bool riseT ) {
dms LST = auxRiseSetTimeLST ( geo - > lat ( ) , righta , decl , riseT ) ;
return dt . GSTtoUT ( geo - > LSTtoGST ( LST ) ) ;
}
dms SkyObject : : auxRiseSetTimeLST ( const dms * gLat , const dms * righta , const dms * decl , bool riseT ) {
dms h0 = elevationCorrection ( ) ;
double H = approxHourAngle ( & h0 , gLat , decl ) ;
dms LST ;
if ( riseT )
LST . setH ( 24.0 + righta - > Hours ( ) - H / 15.0 ) ;
else
LST . setH ( righta - > Hours ( ) + H / 15.0 ) ;
return LST . reduce ( ) ;
}
dms SkyObject : : riseSetTimeAz ( const KStarsDateTime & dt , const GeoLocation * geo , bool riseT ) {
dms Azimuth ;
double AltRad , AzRad ;
double sindec , cosdec , sinlat , coslat , sinHA , cosHA ;
double sinAlt , cosAlt ;
TQTime UT = riseSetTimeUT ( dt , geo , riseT ) ;
KStarsDateTime dt0 = dt ;
dt0 . setTime ( UT ) ;
SkyPoint sp = recomputeCoords ( dt0 , geo ) ;
const dms * ram = sp . ra0 ( ) ;
const dms * decm = sp . dec0 ( ) ;
dms LST = auxRiseSetTimeLST ( geo - > lat ( ) , ram , decm , riseT ) ;
dms HourAngle = dms ( LST . Degrees ( ) - ram - > Degrees ( ) ) ;
geo - > lat ( ) - > SinCos ( sinlat , coslat ) ;
dec ( ) - > SinCos ( sindec , cosdec ) ;
HourAngle . SinCos ( sinHA , cosHA ) ;
sinAlt = sindec * sinlat + cosdec * coslat * cosHA ;
AltRad = asin ( sinAlt ) ;
cosAlt = cos ( AltRad ) ;
AzRad = acos ( ( sindec - sinlat * sinAlt ) / ( coslat * cosAlt ) ) ;
if ( sinHA > 0.0 ) AzRad = 2.0 * dms : : PI - AzRad ; // resolve acos() ambiguity
Azimuth . setRadians ( AzRad ) ;
return Azimuth ;
}
TQTime SkyObject : : transitTimeUT ( const KStarsDateTime & dt , const GeoLocation * geo ) {
dms LST = geo - > GSTtoLST ( dt . gst ( ) ) ;
//dSec is the number of seconds until the object transits.
dms HourAngle = dms ( LST . Degrees ( ) - ra ( ) - > Degrees ( ) ) ;
int dSec = int ( - 3600. * HourAngle . Hours ( ) ) ;
//dt0 is the first guess at the transit time.
KStarsDateTime dt0 = dt . addSecs ( dSec ) ;
//recompute object's position at UT0 and then find
//transit time of this refined position
SkyPoint sp = recomputeCoords ( dt0 , geo ) ;
const dms * ram = sp . ra0 ( ) ;
HourAngle = dms ( LST . Degrees ( ) - ram - > Degrees ( ) ) ;
dSec = int ( - 3600. * HourAngle . Hours ( ) ) ;
return dt . addSecs ( dSec ) . time ( ) ;
}
TQTime SkyObject : : transitTime ( const KStarsDateTime & dt , const GeoLocation * geo ) {
return geo - > UTtoLT ( KStarsDateTime ( dt . date ( ) , transitTimeUT ( dt , geo ) ) ) . time ( ) ;
}
dms SkyObject : : transitAltitude ( const KStarsDateTime & dt , const GeoLocation * geo ) {
KStarsDateTime dt0 = dt ;
TQTime UT = transitTimeUT ( dt , geo ) ;
dt0 . setTime ( UT ) ;
SkyPoint sp = recomputeCoords ( dt0 , geo ) ;
const dms * decm = sp . dec0 ( ) ;
dms delta ;
delta . setRadians ( asin ( sin ( geo - > lat ( ) - > radians ( ) ) *
sin ( decm - > radians ( ) ) +
cos ( geo - > lat ( ) - > radians ( ) ) *
cos ( decm - > radians ( ) ) ) ) ;
return delta ;
}
double SkyObject : : approxHourAngle ( const dms * h0 , const dms * gLat , const dms * dec ) {
double sh0 = sin ( h0 - > radians ( ) ) ;
double r = ( sh0 - sin ( gLat - > radians ( ) ) * sin ( dec - > radians ( ) ) )
/ ( cos ( gLat - > radians ( ) ) * cos ( dec - > radians ( ) ) ) ;
double H = acos ( r ) / dms : : DegToRad ;
return H ;
}
dms SkyObject : : elevationCorrection ( void ) {
/* The atmospheric refraction at the horizon shifts altitude by
* - 34 arcmin = 0.5667 degrees . This value changes if the observer
* is above the horizon , or if the weather conditions change much .
*
* For the sun we have to add half the angular sie of the body , since
* the sunset is the time the upper limb of the sun disappears below
* the horizon , and dawn , when the upper part of the limb appears
* over the horizon . The angular size of the sun = angular size of the
* moon = 31 ' 59 ' ' .
*
* So for the sun the correction is = - 34 - 16 = 50 arcmin = - 0.8333
*
* This same correction should be applied to the moon however parallax
* is important here . Meeus states that the correction should be
* 0.7275 P - 34 arcmin , where P is the moon ' s horizontal parallax .
* He proposes a mean value of 0.125 degrees if no great accuracy
* is needed .
*/
if ( name ( ) = = " Sun " | | name ( ) = = " Moon " )
return dms ( - 0.8333 ) ;
// else if ( name() == "Moon" )
// return dms(0.125);
else // All sources point-like.
return dms ( - 0.5667 ) ;
}
SkyPoint SkyObject : : recomputeCoords ( const KStarsDateTime & dt , const GeoLocation * geo ) {
//store current position
SkyPoint original ( ra ( ) , dec ( ) ) ;
// compute coords for new time jd
KSNumbers num ( dt . djd ( ) ) ;
if ( isSolarSystem ( ) & & geo ) {
dms LST = geo - > GSTtoLST ( dt . gst ( ) ) ;
updateCoords ( & num , true , geo - > lat ( ) , & LST ) ;
} else {
updateCoords ( & num ) ;
}
//the coordinates for the date dt:
SkyPoint sp = SkyPoint ( ra ( ) , dec ( ) ) ;
// restore original coords
setRA ( original . ra ( ) - > Hours ( ) ) ;
setDec ( original . dec ( ) - > Degrees ( ) ) ;
return sp ;
}
bool SkyObject : : checkCircumpolar ( const dms * gLat ) {
double r = - 1.0 * tan ( gLat - > radians ( ) ) * tan ( dec ( ) - > radians ( ) ) ;
if ( r < - 1.0 | | r > 1.0 )
return true ;
else
return false ;
}
TQString SkyObject : : typeName ( void ) const {
if ( Type = = 0 ) return i18n ( " Star " ) ;
else if ( Type = = 1 ) return i18n ( " Catalog Star " ) ;
else if ( Type = = 2 ) return i18n ( " Planet " ) ;
else if ( Type = = 3 ) return i18n ( " Open Cluster " ) ;
else if ( Type = = 4 ) return i18n ( " Globular Cluster " ) ;
else if ( Type = = 5 ) return i18n ( " Gaseous Nebula " ) ;
else if ( Type = = 6 ) return i18n ( " Planetary Nebula " ) ;
else if ( Type = = 7 ) return i18n ( " Supernova Remnant " ) ;
else if ( Type = = 8 ) return i18n ( " Galaxy " ) ;
else if ( Type = = 9 ) return i18n ( " Comet " ) ;
else if ( Type = = 10 ) return i18n ( " Asteroid " ) ;
else return i18n ( " Unknown Type " ) ;
}
void SkyObject : : setName ( const TQString & name ) {
// if (name == "star" ) kdDebug() << "name == star" << endl;
delete Name ;
if ( ! name . isEmpty ( ) )
Name = new TQString ( name ) ;
else
{ Name = 0 ; /*kdDebug() << "name saved" << endl;*/ }
}
void SkyObject : : setName2 ( const TQString & name2 ) {
delete Name2 ;
if ( ! name2 . isEmpty ( ) )
Name2 = new TQString ( name2 ) ;
else
{ Name2 = 0 ; /*kdDebug() << "name2 saved" << endl;*/ }
}
TQString SkyObject : : messageFromTitle ( const TQString & imageTitle ) {
TQString message = imageTitle ;
//HST Image
if ( imageTitle = = i18n ( " Show HST Image " ) | | imageTitle . contains ( " HST " ) ) {
message = i18n ( " %1: Hubble Space Telescope, operated by STScI for NASA [public domain] " ) . arg ( longname ( ) ) ;
//Spitzer Image
} else if ( imageTitle . contains ( i18n ( " Show Spitzer Image " ) ) ) {
message = i18n ( " %1: Spitzer Space Telescope, courtesy NASA/JPL-Caltech [public domain] " ) . arg ( longname ( ) ) ;
//SEDS Image
} else if ( imageTitle = = i18n ( " Show SEDS Image " ) ) {
message = i18n ( " %1: SEDS, http://www.seds.org [free for non-commercial use] " ) . arg ( longname ( ) ) ;
//Kitt Peak AOP Image
} else if ( imageTitle = = i18n ( " Show KPNO AOP Image " ) ) {
message = i18n ( " %1: Advanced Observing Program at Kitt Peak National Observatory [free for non-commercial use; no physical reproductions] " ) . arg ( longname ( ) ) ;
//NOAO Image
} else if ( imageTitle . contains ( i18n ( " Show NOAO Image " ) ) ) {
message = i18n ( " %1: National Optical Astronomy Observatories and AURA [free for non-commercial use] " ) . arg ( longname ( ) ) ;
//VLT Image
} else if ( imageTitle . contains ( " VLT " ) ) {
message = i18n ( " %1: Very Large Telescope, operated by the European Southern Observatory [free for non-commercial use; no reproductions] " ) . arg ( longname ( ) ) ;
//All others
} else if ( imageTitle . startsWith ( i18n ( " Show " ) ) ) {
message = imageTitle . mid ( imageTitle . find ( " " ) + 1 ) ; //eat first word, "Show"
message = longname ( ) + " : " + message ;
}
return message ;
}
//New saveUserLog, moved from DetailDialog.
//Should create a special UserLog widget that encapsulates the "default"
//message in the widget when no log exists (much like we do with dmsBox now)
void SkyObject : : saveUserLog ( const TQString & newLog ) {
TQFile file ;
TQString logs ; //existing logs
//Do nothing if new log is the "default" message
//(keep going if new log is empty; we'll want to delete its current entry)
if ( newLog = = ( i18n ( " Record here observation logs and/or data on %1. " ) . arg ( name ( ) ) ) | | newLog . isEmpty ( ) )
return ;
// header label
TQString KSLabel = " [KSLABEL: " + name ( ) + " ] " ;
//However, we can't accept a star name if it has a greek letter in it:
if ( type ( ) = = STAR ) {
StarObject * star = ( StarObject * ) this ;
if ( name ( ) = = star - > gname ( ) )
KSLabel = " [KSLABEL: " + star - > gname ( false ) + " ] " ; //"false": spell out greek letter
}
file . setName ( locateLocal ( " appdata " , " userlog.dat " ) ) ; //determine filename in local user KDE directory tree.
if ( file . open ( IO_ReadOnly ) ) {
TQTextStream instream ( & file ) ;
// read all data into memory
logs = instream . read ( ) ;
file . close ( ) ;
}
//Remove old log entry from the logs text
if ( ! userLog . isEmpty ( ) ) {
int startIndex , endIndex ;
TQString sub ;
startIndex = logs . find ( KSLabel ) ;
sub = logs . mid ( startIndex ) ;
endIndex = sub . find ( " [KSLogEnd] " ) ;
logs . remove ( startIndex , endIndex + 11 ) ;
}
//append the new log entry to the end of the logs text,
//but only if the log is not empty
if ( ! newLog . stripWhiteSpace ( ) . isEmpty ( ) )
logs . append ( KSLabel + " \n " + newLog + " \n [KSLogEnd] \n " ) ;
//Open file for writing
//FIXME: change error message to "cannot write to user log file"
if ( ! file . open ( IO_WriteOnly ) ) {
kdDebug ( ) < < i18n ( " user log file could not be opened. " ) < < endl ;
return ;
}
//Write new logs text
TQTextStream outstream ( & file ) ;
outstream < < logs ;
//Set the log text in the object itself.
userLog = newLog ;
file . close ( ) ;
}