File indexing completed on 2024-04-14 03:43:13

0001 /*
0002     SPDX-FileCopyrightText: 2001 Jason Harris <jharris@30doradus.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #pragma once
0008 
0009 #include "dms.h"
0010 #include "skypoint.h"
0011 
0012 #include <KLocalizedString>
0013 
0014 #include <QSharedDataPointer>
0015 #include <QString>
0016 #include <QStringList>
0017 
0018 class QPoint;
0019 class GeoLocation;
0020 class KSPopupMenu;
0021 
0022 namespace {
0023     constexpr const char *emptyString = "";
0024     constexpr const char *unnamedString       = I18N_NOOP("unnamed");
0025     constexpr const char *unnamedObjectString = I18N_NOOP("unnamed object");
0026     constexpr const char *starString          = I18N_NOOP("star");
0027 }
0028 
0029 // Set the faintest sane magnitude to 36.0 (faintest visual magnitude visible with E-ELT, acc. to Wikipedia on Apparent Magnitude.)
0030 constexpr const float FAINTEST_MAGNITUDE = 36.0;
0031 
0032 /**
0033  * @class SkyObject
0034  * Provides all necessary information about an object in the sky:
0035  * its coordinates, name(s), type, magnitude, and QStringLists of
0036  * URLs for images and webpages regarding the object.
0037  * @short Information about an object in the sky.
0038  * @author Jason Harris
0039  * @version 1.0
0040  */
0041 class SkyObject : public SkyPoint
0042 {
0043   public:
0044     /**
0045      * @short Type for Unique object IDenticator.
0046      *
0047      * Each object has unique ID (UID). For different objects UIDs must be different.
0048      */
0049     typedef qint64 UID;
0050 
0051     /** @short Kind of UID */
0052     static const UID UID_STAR;
0053     static const UID UID_GALAXY;
0054     static const UID UID_DEEPSKY;
0055     static const UID UID_SOLARSYS;
0056 
0057     /** Invalid UID. Real sky object could not have such UID */
0058     static const UID invalidUID;
0059 
0060     /**
0061      * Constructor.  Set SkyObject data according to arguments.
0062      * @param t Type of object
0063      * @param r catalog Right Ascension
0064      * @param d catalog Declination
0065      * @param m magnitude (brightness)
0066      * @param n Primary name
0067      * @param n2 Secondary name
0068      * @param lname Long name (common name)
0069      */
0070     explicit SkyObject(int t = TYPE_UNKNOWN, dms r = dms(0.0), dms d = dms(0.0), float m = 0.0,
0071                        const QString &n = QString(), const QString &n2 = QString(), const QString &lname = QString());
0072     /**
0073      * Constructor. Set SkyObject data according to arguments. Differs from
0074      * above function only in data type of RA and Dec.
0075      * @param t Type of object
0076      * @param r catalog Right Ascension
0077      * @param d catalog Declination
0078      * @param m magnitude (brightness)
0079      * @param n Primary name
0080      * @param n2 Secondary name
0081      * @param lname Long name (common name)
0082      */
0083     SkyObject(int t, double r, double d, float m = 0.0, const QString &n = QString(), const QString &n2 = QString(),
0084               const QString &lname = QString());
0085 
0086     /** Destructor (empty) */
0087     virtual ~SkyObject() override = default;
0088 
0089     /**
0090      * @short Create copy of object.
0091      * This method is virtual copy constructor. It allows for safe
0092      * copying of objects. In other words, KSPlanet object stored in
0093      * SkyObject pointer will be copied as KSPlanet.
0094      *
0095      * Each subclass of SkyObject MUST implement clone method. There
0096      * is no checking to ensure this, though.
0097      *
0098      *  @return pointer to newly allocated object. Caller takes full responsibility
0099      *  for deallocating it.
0100      */
0101     virtual SkyObject *clone() const;
0102 
0103     /**
0104      * @enum TYPE
0105      * The type classification of the SkyObject.
0106      * @note Keep TYPE_UNKNOWN at 255. To find out how many known
0107      * types exist, keep the NUMBER_OF_KNOWN_TYPES at the highest
0108      * non-Unknown value. This is a fake type that can be used in
0109      * comparisons and for loops.
0110      */
0111     enum TYPE
0112     {
0113         STAR                  = 0,
0114         CATALOG_STAR          = 1,
0115         PLANET                = 2,
0116         OPEN_CLUSTER          = 3,
0117         GLOBULAR_CLUSTER      = 4,
0118         GASEOUS_NEBULA        = 5,
0119         PLANETARY_NEBULA      = 6,
0120         SUPERNOVA_REMNANT     = 7,
0121         GALAXY                = 8,
0122         COMET                 = 9,
0123         ASTEROID              = 10,
0124         CONSTELLATION         = 11,
0125         MOON                  = 12,
0126         ASTERISM              = 13,
0127         GALAXY_CLUSTER        = 14,
0128         DARK_NEBULA           = 15,
0129         QUASAR                = 16,
0130         MULT_STAR             = 17,
0131         RADIO_SOURCE          = 18,
0132         SATELLITE             = 19,
0133         SUPERNOVA             = 20,
0134         NUMBER_OF_KNOWN_TYPES = 21,
0135         TYPE_UNKNOWN          = 255
0136     };
0137     /**
0138      * @return A translated string indicating the type name for a given type number
0139      * @param t The type number
0140      * @note Note the existence of a SkyObject::typeName( void ) method that is not static and returns the type of this object.
0141      */
0142     static QString typeName(const int t);
0143 
0144     /** @return object's primary name. */
0145     inline virtual QString name(void) const { return hasName() ? Name : unnamedString; }
0146 
0147     /** @return object's primary name, translated to local language. */
0148     inline QString translatedName() const
0149     {
0150         return i18n(
0151             name()
0152                 .toUtf8()); // FIXME: Hmm... that's funny. How does the string extraction work, if we are UTF8-ing the name first? Does the string extraction change to UTF8?
0153     }
0154 
0155     /** @return object's secondary name */
0156     inline QString name2(void) const { return (hasName2() ? Name2 : emptyString); }
0157 
0158     /** @return object's secondary name, translated to local language. */
0159     inline QString translatedName2() const { return (hasName2() ? i18n(Name2.toUtf8()) : emptyString); }
0160 
0161     /**
0162      * @return object's common (long) name
0163      */
0164     virtual QString longname(void) const { return hasLongName() ? LongName : unnamedObjectString; }
0165 
0166     /**
0167      * @return object's common (long) name, translated to local language.
0168      */
0169     QString translatedLongName() const { return i18n(longname().toUtf8()); }
0170 
0171     /**
0172      * Set the object's long name.
0173      * @param longname the object's long name.
0174      */
0175     void setLongName(const QString &longname = QString());
0176 
0177     /**
0178      * @return the string used to label the object on the map
0179      * In the default implementation, this just returns translatedName()
0180      * Overridden by StarObject.
0181      */
0182     virtual QString labelString() const;
0183 
0184     /**
0185      * @return object's type identifier (int)
0186      * @see enum TYPE
0187      */
0188     inline int type(void) const { return (int)Type; }
0189 
0190     /**
0191      * Set the object's type identifier to the argument.
0192      * @param t the object's type identifier (e.g., "SkyObject::PLANETARY_NEBULA")
0193      * @see enum TYPE
0194      */
0195     inline void setType(int t) { Type = (unsigned char)t; }
0196 
0197     /**
0198      * @return the type name for this object
0199      * @note This just calls the static method by the same name, with the appropriate type number. See SkyObject::typeName( const int )
0200      */
0201     QString typeName() const;
0202 
0203     /**
0204      * @return object's magnitude
0205      */
0206     inline float mag() const { return sortMagnitude; }
0207 
0208     /**
0209      * @return the object's position angle.  This is overridden in KSPlanetBase
0210      * and DeepSkyObject; for all other SkyObjects, this returns 0.0.
0211      */
0212     inline virtual double pa() const { return 0.0; }
0213 
0214     /**
0215      * @return true if the object is a solar system body.
0216      */
0217     inline bool isSolarSystem() const { return (type() == 2 || type() == 9 || type() == 10 || type() == 12); }
0218 
0219     /**
0220      * Initialize the popup menut. This function should call correct
0221      * initialization function in KSPopupMenu. By overloading the
0222      * function, we don't have to check the object type when we need
0223      * the menu.
0224      */
0225     virtual void initPopupMenu(KSPopupMenu *pmenu);
0226 
0227     /** Show Type-specific popup menu. Overloading is done in the function initPopupMenu */
0228     void showPopupMenu(KSPopupMenu *pmenu, const QPoint &pos);
0229 
0230     /**
0231      * Determine the time at which the point will rise or set.  Because solar system
0232      * objects move across the sky, it is necessary to iterate on the solution.
0233      * We compute the rise/set time for the object's current position, then
0234      * compute the object's position at that time.  Finally, we recompute then
0235      * rise/set time for the new coordinates.  Further iteration is not necessary,
0236      * even for the most swiftly-moving object (the Moon).
0237      * @return the local time that the object will rise
0238      * @param dt current UT date/time
0239      * @param geo current geographic location
0240      * @param rst If true, compute rise time. If false, compute set time.
0241      * @param exact If true, use a second iteration for more accurate time
0242      */
0243     QTime riseSetTime(const KStarsDateTime &dt, const GeoLocation *geo, bool rst, bool exact = true) const;
0244 
0245     /**
0246      * @return the UT time when the object will rise or set
0247      * @param dt  target date/time
0248      * @param geo pointer to Geographic location
0249      * @param rst Boolean. If true will compute rise time. If false
0250      * will compute set time.
0251      * @param exact If true, use a second iteration for more accurate time
0252      */
0253     QTime riseSetTimeUT(const KStarsDateTime &dt, const GeoLocation *geo, bool rst, bool exact = true) const;
0254 
0255     /**
0256      * @return the Azimuth time when the object will rise or set. This function
0257      * recomputes set or rise UT times.
0258      * @param dt  target date/time
0259      * @param geo GeoLocation object
0260      * @param rst Boolen. If true will compute rise time. If false
0261      * will compute set time.
0262      */
0263     dms riseSetTimeAz(const KStarsDateTime &dt, const GeoLocation *geo, bool rst) const;
0264 
0265     /**
0266      * The same iteration technique described in riseSetTime() is used here.
0267      * @return the local time that the object will transit the meridian.
0268      * @param dt  target date/time
0269      * @param geo pointer to the geographic location
0270      */
0271     QTime transitTime(const KStarsDateTime &dt, const GeoLocation *geo) const;
0272 
0273     /**
0274      * @return the universal time that the object will transit the meridian.
0275      * @param dt   target date/time
0276      * @param geo pointer to the geographic location
0277      */
0278     QTime transitTimeUT(const KStarsDateTime &dt, const GeoLocation *geo) const;
0279 
0280     /**
0281      * @return the altitude of the object at the moment it transits the meridian.
0282      * @param dt  target date/time
0283      * @param geo pointer to the geographic location
0284      */
0285     dms transitAltitude(const KStarsDateTime &dt, const GeoLocation *geo) const;
0286 
0287     /**
0288      * The equatorial coordinates for the object on date dt are computed and returned,
0289      * but the object's internal coordinates are not modified.
0290      * @return the coordinates of the selected object for the time given by jd
0291      * @param dt  date/time for which the coords will be computed.
0292      * @param geo pointer to geographic location (used for solar system only)
0293      * @note Does not update the horizontal coordinates. Call EquatorialToHorizontal for that.
0294      */
0295     SkyPoint recomputeCoords(const KStarsDateTime &dt, const GeoLocation *geo = nullptr) const;
0296 
0297     /**
0298      * @short Like recomputeCoords, but also calls EquatorialToHorizontal before returning
0299      */
0300     SkyPoint recomputeHorizontalCoords(const KStarsDateTime &dt, const GeoLocation *geo) const;
0301 
0302     inline bool hasName() const { return !Name.isEmpty(); }
0303 
0304     inline bool hasName2() const { return !Name2.isEmpty(); }
0305 
0306     inline bool hasLongName() const { return !LongName.isEmpty(); }
0307 
0308     /**
0309      * @short Given the Image title from a URL file, try to convert it to an image credit string.
0310      */
0311     QString messageFromTitle(const QString &imageTitle) const;
0312 
0313     /**
0314      * @return the pixel distance for offseting the object's name label
0315      * @note overridden in StarObject, DeepSkyObject, KSPlanetBase
0316      */
0317     virtual double labelOffset() const;
0318 
0319     /**
0320      * @short Return UID for object.
0321      * This method should be reimplemented in all concrete
0322      * subclasses. Implementation for SkyObject just returns
0323      * invalidUID. It's required SkyObject is not an abstract class.
0324      */
0325     virtual UID getUID() const;
0326 
0327     // TODO: (Valentin) have another think about onFocus handlers :)    
0328 
0329     /**
0330      * @brief hashBeenUpdated
0331      * @return whether the coordinates of the object have been updated
0332      *
0333      * This is used for faster filtering.
0334      */
0335     bool hashBeenUpdated() { return has_been_updated; }
0336 
0337   private:
0338     /**
0339      * Compute the UT time when the object will rise or set. It is an auxiliary
0340      * procedure because it does not use the RA and DEC of the object but values
0341      * given as parameters. You may want to use riseSetTimeUT() which is
0342      * public.  riseSetTimeUT() calls this function iteratively.
0343      * @param dt     target date/time
0344      * @param geo    pointer to Geographic location
0345      * @param righta pointer to Right ascention of the object
0346      * @param decl   pointer to Declination of the object
0347      * @param rst    Boolean. If true will compute rise time. If false
0348      * will compute set time.
0349      * @return the time at which the given position will rise or set.
0350      */
0351     QTime auxRiseSetTimeUT(const KStarsDateTime &dt, const GeoLocation *geo, const dms *righta, const dms *decl,
0352                            bool riseT) const;
0353 
0354     /**
0355      * Compute the LST time when the object will rise or set. It is an auxiliary
0356      * procedure because it does not use the RA and DEC of the object but values
0357      * given as parameters. You may want to use riseSetTimeLST() which is
0358      * public.  riseSetTimeLST() calls this function iteratively.
0359      * @param gLt Geographic latitude
0360      * @param rga Right ascention of the object
0361      * @param decl Declination of the object
0362      * @param rst Boolean. If true will compute rise time. If false
0363      * will compute set time.
0364      */
0365     dms auxRiseSetTimeLST(const dms *gLt, const dms *rga, const dms *decl, bool rst) const;
0366 
0367     /**
0368      * Compute the approximate hour angle that an object with declination d will have
0369      * when its altitude is h (as seen from geographic latitude gLat).
0370      * This function is only used by auxRiseSetTimeLST().
0371      * @param h pointer to the altitude of the object
0372      * @param gLat pointer to the geographic latitude
0373      * @param d pointer to the declination of the object.
0374      * @return the Hour Angle, in degrees.
0375      */
0376     double approxHourAngle(const dms *h, const dms *gLat, const dms *d) const;
0377 
0378     /**
0379      * Correct for the geometric altitude of the center of the body at the
0380      * time of rising or setting. This is due to refraction at the horizon
0381      * and to the size of the body. The moon correction has also to take into
0382      * account parallax. The value we use here is a rough approximation
0383      * suggested by J. Meeus.
0384      *
0385      * Weather status (temperature and pressure basically) is not taken
0386      * into account although change of conditions between summer and
0387      * winter could shift the times of sunrise and sunset by 20 seconds.
0388      *
0389      * This function is only used by auxRiseSetTimeLST().
0390      * @return dms object with the correction.
0391      */
0392     dms elevationCorrection(void) const;
0393 
0394     unsigned char Type;
0395     float
0396         sortMagnitude; // This magnitude is used for sorting / making decisions about the visibility of an object. Should not be NaN.
0397 
0398   protected:
0399     /**
0400      * Set the object's sorting magnitude.
0401      * @param m the object's magnitude.
0402      */
0403     inline void setMag(float m)
0404     {
0405         sortMagnitude =
0406             m < FAINTEST_MAGNITUDE ?
0407                 m :
0408                 NaN::
0409                     f;
0410     }
0411     // FIXME: We claim sortMagnitude should not be NaN, but we are setting it to NaN above!! ^
0412 
0413     /**
0414      * Set the object's primary name.
0415      * @param name the object's primary name
0416      */
0417     inline void setName(const QString &name) { Name = name; }
0418 
0419     /**
0420      * Set the object's secondary name.
0421      * @param name2 the object's secondary name.
0422      */
0423     inline void setName2(const QString &name2 = QString()) { Name2 = name2; }
0424 
0425     QString Name, Name2, LongName;
0426 
0427     // Whether the coordinates of the object have been updated.
0428     // The default value is chose for compatibility reasons.
0429     // It primarily matters for objects which are filtered.
0430     // See `KSAsteroid` for an example.
0431     bool has_been_updated = true;
0432 };