1 /*
2     SPDX-FileCopyrightText: 2001 Jason Harris <jharris@30doradus.org>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #pragma once
8 
9 #include "dms.h"
10 #include "skypoint.h"
11 
12 #include <KLocalizedString>
13 
14 #include <QSharedDataPointer>
15 #include <QString>
16 #include <QStringList>
17 
18 class QPoint;
19 class GeoLocation;
20 class KSPopupMenu;
21 
22 /**
23  * @class SkyObject
24  * Provides all necessary information about an object in the sky:
25  * its coordinates, name(s), type, magnitude, and QStringLists of
26  * URLs for images and webpages regarding the object.
27  * @short Information about an object in the sky.
28  * @author Jason Harris
29  * @version 1.0
30  */
31 class SkyObject : public SkyPoint
32 {
33   public:
34     /**
35      * @short Type for Unique object IDenticator.
36      *
37      * Each object has unique ID (UID). For different objects UIDs must be different.
38      */
39     typedef qint64 UID;
40 
41     /** @short Kind of UID */
42     static const UID UID_STAR;
43     static const UID UID_GALAXY;
44     static const UID UID_DEEPSKY;
45     static const UID UID_SOLARSYS;
46 
47     /** Invalid UID. Real sky object could not have such UID */
48     static const UID invalidUID;
49 
50     /**
51      * Constructor.  Set SkyObject data according to arguments.
52      * @param t Type of object
53      * @param r catalog Right Ascension
54      * @param d catalog Declination
55      * @param m magnitude (brightness)
56      * @param n Primary name
57      * @param n2 Secondary name
58      * @param lname Long name (common name)
59      */
60     explicit SkyObject(int t = TYPE_UNKNOWN, dms r = dms(0.0), dms d = dms(0.0), float m = 0.0,
61                        const QString &n = QString(), const QString &n2 = QString(), const QString &lname = QString());
62     /**
63      * Constructor. Set SkyObject data according to arguments. Differs from
64      * above function only in data type of RA and Dec.
65      * @param t Type of object
66      * @param r catalog Right Ascension
67      * @param d catalog Declination
68      * @param m magnitude (brightness)
69      * @param n Primary name
70      * @param n2 Secondary name
71      * @param lname Long name (common name)
72      */
73     SkyObject(int t, double r, double d, float m = 0.0, const QString &n = QString(), const QString &n2 = QString(),
74               const QString &lname = QString());
75 
76     /** Destructor (empty) */
77     virtual ~SkyObject() override = default;
78 
79     /**
80      * @short Create copy of object.
81      * This method is virtual copy constructor. It allows for safe
82      * copying of objects. In other words, KSPlanet object stored in
83      * SkyObject pointer will be copied as KSPlanet.
84      *
85      * Each subclass of SkyObject MUST implement clone method. There
86      * is no checking to ensure this, though.
87      *
88      *  @return pointer to newly allocated object. Caller takes full responsibility
89      *  for deallocating it.
90      */
91     virtual SkyObject *clone() const;
92 
93     /**
94      * @enum TYPE
95      * The type classification of the SkyObject.
96      * @note Keep TYPE_UNKNOWN at 255. To find out how many known
97      * types exist, keep the NUMBER_OF_KNOWN_TYPES at the highest
98      * non-Unknown value. This is a fake type that can be used in
99      * comparisons and for loops.
100      */
101     enum TYPE
102     {
103         STAR                  = 0,
104         CATALOG_STAR          = 1,
105         PLANET                = 2,
106         OPEN_CLUSTER          = 3,
107         GLOBULAR_CLUSTER      = 4,
108         GASEOUS_NEBULA        = 5,
109         PLANETARY_NEBULA      = 6,
110         SUPERNOVA_REMNANT     = 7,
111         GALAXY                = 8,
112         COMET                 = 9,
113         ASTEROID              = 10,
114         CONSTELLATION         = 11,
115         MOON                  = 12,
116         ASTERISM              = 13,
117         GALAXY_CLUSTER        = 14,
118         DARK_NEBULA           = 15,
119         QUASAR                = 16,
120         MULT_STAR             = 17,
121         RADIO_SOURCE          = 18,
122         SATELLITE             = 19,
123         SUPERNOVA             = 20,
124         NUMBER_OF_KNOWN_TYPES = 21,
125         TYPE_UNKNOWN          = 255
126     };
127     /**
128      * @return A translated string indicating the type name for a given type number
129      * @param t The type number
130      * @note Note the existence of a SkyObject::typeName( void ) method that is not static and returns the type of this object.
131      */
132     static QString typeName(const int t);
133 
134     /** @return object's primary name. */
name(void)135     inline virtual QString name(void) const { return hasName() ? Name : unnamedString; }
136 
137     /** @return object's primary name, translated to local language. */
translatedName()138     inline QString translatedName() const
139     {
140         return i18n(
141             name()
142                 .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?
143     }
144 
145     /** @return object's secondary name */
name2(void)146     inline QString name2(void) const { return (hasName2() ? Name2 : emptyString); }
147 
148     /** @return object's secondary name, translated to local language. */
translatedName2()149     inline QString translatedName2() const { return (hasName2() ? i18n(Name2.toUtf8()) : emptyString); }
150 
151     /**
152      * @return object's common (long) name
153      */
longname(void)154     virtual QString longname(void) const { return hasLongName() ? LongName : unnamedObjectString; }
155 
156     /**
157      * @return object's common (long) name, translated to local language.
158      */
translatedLongName()159     QString translatedLongName() const { return i18n(longname().toUtf8()); }
160 
161     /**
162      * Set the object's long name.
163      * @param longname the object's long name.
164      */
165     void setLongName(const QString &longname = QString());
166 
167     /**
168      * @return the string used to label the object on the map
169      * In the default implementation, this just returns translatedName()
170      * Overridden by StarObject.
171      */
172     virtual QString labelString() const;
173 
174     /**
175      * @return object's type identifier (int)
176      * @see enum TYPE
177      */
type(void)178     inline int type(void) const { return (int)Type; }
179 
180     /**
181      * Set the object's type identifier to the argument.
182      * @param t the object's type identifier (e.g., "SkyObject::PLANETARY_NEBULA")
183      * @see enum TYPE
184      */
setType(int t)185     inline void setType(int t) { Type = (unsigned char)t; }
186 
187     /**
188      * @return the type name for this object
189      * @note This just calls the static method by the same name, with the appropriate type number. See SkyObject::typeName( const int )
190      */
191     QString typeName() const;
192 
193     /**
194      * @return object's magnitude
195      */
mag()196     inline float mag() const { return sortMagnitude; }
197 
198     /**
199      * @return the object's position angle.  This is overridden in KSPlanetBase
200      * and DeepSkyObject; for all other SkyObjects, this returns 0.0.
201      */
pa()202     inline virtual double pa() const { return 0.0; }
203 
204     /**
205      * @return true if the object is a solar system body.
206      */
isSolarSystem()207     inline bool isSolarSystem() const { return (type() == 2 || type() == 9 || type() == 10 || type() == 12); }
208 
209     /**
210      * Initialize the popup menut. This function should call correct
211      * initialization function in KSPopupMenu. By overloading the
212      * function, we don't have to check the object type when we need
213      * the menu.
214      */
215     virtual void initPopupMenu(KSPopupMenu *pmenu);
216 
217     /** Show Type-specific popup menu. Overloading is done in the function initPopupMenu */
218     void showPopupMenu(KSPopupMenu *pmenu, const QPoint &pos);
219 
220     /**
221      * Determine the time at which the point will rise or set.  Because solar system
222      * objects move across the sky, it is necessary to iterate on the solution.
223      * We compute the rise/set time for the object's current position, then
224      * compute the object's position at that time.  Finally, we recompute then
225      * rise/set time for the new coordinates.  Further iteration is not necessary,
226      * even for the most swiftly-moving object (the Moon).
227      * @return the local time that the object will rise
228      * @param dt current UT date/time
229      * @param geo current geographic location
230      * @param rst If true, compute rise time. If false, compute set time.
231      * @param exact If true, use a second iteration for more accurate time
232      */
233     QTime riseSetTime(const KStarsDateTime &dt, const GeoLocation *geo, bool rst, bool exact = true) const;
234 
235     /**
236      * @return the UT time when the object will rise or set
237      * @param dt  target date/time
238      * @param geo pointer to Geographic location
239      * @param rst Boolean. If true will compute rise time. If false
240      * will compute set time.
241      * @param exact If true, use a second iteration for more accurate time
242      */
243     QTime riseSetTimeUT(const KStarsDateTime &dt, const GeoLocation *geo, bool rst, bool exact = true) const;
244 
245     /**
246      * @return the Azimuth time when the object will rise or set. This function
247      * recomputes set or rise UT times.
248      * @param dt  target date/time
249      * @param geo GeoLocation object
250      * @param rst Boolen. If true will compute rise time. If false
251      * will compute set time.
252      */
253     dms riseSetTimeAz(const KStarsDateTime &dt, const GeoLocation *geo, bool rst) const;
254 
255     /**
256      * The same iteration technique described in riseSetTime() is used here.
257      * @return the local time that the object will transit the meridian.
258      * @param dt  target date/time
259      * @param geo pointer to the geographic location
260      */
261     QTime transitTime(const KStarsDateTime &dt, const GeoLocation *geo) const;
262 
263     /**
264      * @return the universal time that the object will transit the meridian.
265      * @param dt   target date/time
266      * @param geo pointer to the geographic location
267      */
268     QTime transitTimeUT(const KStarsDateTime &dt, const GeoLocation *geo) const;
269 
270     /**
271      * @return the altitude of the object at the moment it transits the meridian.
272      * @param dt  target date/time
273      * @param geo pointer to the geographic location
274      */
275     dms transitAltitude(const KStarsDateTime &dt, const GeoLocation *geo) const;
276 
277     /**
278      * The equatorial coordinates for the object on date dt are computed and returned,
279      * but the object's internal coordinates are not modified.
280      * @return the coordinates of the selected object for the time given by jd
281      * @param dt  date/time for which the coords will be computed.
282      * @param geo pointer to geographic location (used for solar system only)
283      * @note Does not update the horizontal coordinates. Call EquatorialToHorizontal for that.
284      */
285     SkyPoint recomputeCoords(const KStarsDateTime &dt, const GeoLocation *geo = nullptr) const;
286 
287     /**
288      * @short Like recomputeCoords, but also calls EquatorialToHorizontal before returning
289      */
290     SkyPoint recomputeHorizontalCoords(const KStarsDateTime &dt, const GeoLocation *geo) const;
291 
hasName()292     inline bool hasName() const { return !Name.isEmpty(); }
293 
hasName2()294     inline bool hasName2() const { return !Name2.isEmpty(); }
295 
hasLongName()296     inline bool hasLongName() const { return !LongName.isEmpty(); }
297 
298     /**
299      * @short Given the Image title from a URL file, try to convert it to an image credit string.
300      */
301     QString messageFromTitle(const QString &imageTitle) const;
302 
303     /**
304      * @return the pixel distance for offseting the object's name label
305      * @note overridden in StarObject, DeepSkyObject, KSPlanetBase
306      */
307     virtual double labelOffset() const;
308 
309     /**
310      * @short Return UID for object.
311      * This method should be reimplemented in all concrete
312      * subclasses. Implementation for SkyObject just returns
313      * invalidUID. It's required SkyObject is not an abstract class.
314      */
315     virtual UID getUID() const;
316 
317     // TODO: (Valentin) have another think about onFocus handlers :)
318 
319     /**
320      * @brief hashBeenUpdated
321      * @return whether the coordinates of the object have been updated
322      *
323      * This is used for faster filtering.
324      */
hashBeenUpdated()325     bool hashBeenUpdated() { return has_been_updated; }
326 
327   private:
328     /**
329      * Compute the UT time when the object will rise or set. It is an auxiliary
330      * procedure because it does not use the RA and DEC of the object but values
331      * given as parameters. You may want to use riseSetTimeUT() which is
332      * public.  riseSetTimeUT() calls this function iteratively.
333      * @param dt     target date/time
334      * @param geo    pointer to Geographic location
335      * @param righta pointer to Right ascention of the object
336      * @param decl   pointer to Declination of the object
337      * @param rst    Boolean. If true will compute rise time. If false
338      * will compute set time.
339      * @return the time at which the given position will rise or set.
340      */
341     QTime auxRiseSetTimeUT(const KStarsDateTime &dt, const GeoLocation *geo, const dms *righta, const dms *decl,
342                            bool riseT) const;
343 
344     /**
345      * Compute the LST time when the object will rise or set. It is an auxiliary
346      * procedure because it does not use the RA and DEC of the object but values
347      * given as parameters. You may want to use riseSetTimeLST() which is
348      * public.  riseSetTimeLST() calls this function iteratively.
349      * @param gLt Geographic latitude
350      * @param rga Right ascention of the object
351      * @param decl Declination of the object
352      * @param rst Boolean. If true will compute rise time. If false
353      * will compute set time.
354      */
355     dms auxRiseSetTimeLST(const dms *gLt, const dms *rga, const dms *decl, bool rst) const;
356 
357     /**
358      * Compute the approximate hour angle that an object with declination d will have
359      * when its altitude is h (as seen from geographic latitude gLat).
360      * This function is only used by auxRiseSetTimeLST().
361      * @param h pointer to the altitude of the object
362      * @param gLat pointer to the geographic latitude
363      * @param d pointer to the declination of the object.
364      * @return the Hour Angle, in degrees.
365      */
366     double approxHourAngle(const dms *h, const dms *gLat, const dms *d) const;
367 
368     /**
369      * Correct for the geometric altitude of the center of the body at the
370      * time of rising or setting. This is due to refraction at the horizon
371      * and to the size of the body. The moon correction has also to take into
372      * account parallax. The value we use here is a rough approximation
373      * suggested by J. Meeus.
374      *
375      * Weather status (temperature and pressure basically) is not taken
376      * into account although change of conditions between summer and
377      * winter could shift the times of sunrise and sunset by 20 seconds.
378      *
379      * This function is only used by auxRiseSetTimeLST().
380      * @return dms object with the correction.
381      */
382     dms elevationCorrection(void) const;
383 
384     unsigned char Type;
385     float
386         sortMagnitude; // This magnitude is used for sorting / making decisions about the visibility of an object. Should not be NaN.
387 
388   protected:
389     /**
390      * Set the object's sorting magnitude.
391      * @param m the object's magnitude.
392      */
setMag(float m)393     inline void setMag(float m)
394     {
395         sortMagnitude =
396             m < 36.0 ?
397                 m :
398                 NaN::
399                     f; // Updating faintest sane magnitude to 36.0 (faintest visual magnitude visible with E-ELT, acc. to Wikipedia on Apparent Magnitude.)
400     }
401     // FIXME: We claim sortMagnitude should not be NaN, but we are setting it to NaN above!! ^
402 
403     /**
404      * Set the object's primary name.
405      * @param name the object's primary name
406      */
setName(const QString & name)407     inline void setName(const QString &name) { Name = name; }
408 
409     /**
410      * Set the object's secondary name.
411      * @param name2 the object's secondary name.
412      */
413     inline void setName2(const QString &name2 = QString()) { Name2 = name2; }
414 
415     QString Name, Name2, LongName;
416 
417     // store often used name strings in static variables
418     static QString emptyString;
419     static QString unnamedString;
420     static QString unnamedObjectString;
421     static QString starString;
422 
423     // Whether the coordinates of the object have been updated.
424     // The default value is chose for compatibility reasons.
425     // It primarily matters for objects which are filtered.
426     // See `KSAsteroid` for an example.
427     bool has_been_updated = true;
428 };
429