1 /*
2 SPDX-FileCopyrightText: 2001 Jason Harris <jharris@30doradus.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "skyobject.h"
8
9 #include "geolocation.h"
10 #include "ksnumbers.h"
11 #include "kspaths.h"
12 #ifdef KSTARS_LITE
13 #include "skymaplite.h"
14 #else
15 #include "kspopupmenu.h"
16 #include "skymap.h"
17 #endif
18 #include "kstarsdata.h"
19 #include "Options.h"
20 #include "starobject.h"
21 #include "skycomponents/skylabeler.h"
22
23
24 QString SkyObject::emptyString;
25 QString SkyObject::unnamedString = QString(I18N_NOOP("unnamed"));
26 QString SkyObject::unnamedObjectString = QString(I18N_NOOP("unnamed object"));
27 QString SkyObject::starString = QString("star");
28
29 const SkyObject::UID SkyObject::invalidUID = ~0;
30 const SkyObject::UID SkyObject::UID_STAR = 0;
31 const SkyObject::UID SkyObject::UID_GALAXY = 1;
32 const SkyObject::UID SkyObject::UID_DEEPSKY = 2;
33 const SkyObject::UID SkyObject::UID_SOLARSYS = 3;
34
SkyObject(int t,dms r,dms d,float m,const QString & n,const QString & n2,const QString & lname)35 SkyObject::SkyObject(int t, dms r, dms d, float m, const QString &n, const QString &n2, const QString &lname)
36 : SkyPoint(r, d)
37 {
38 setType(t);
39 sortMagnitude = m;
40 setName(n);
41 setName2(n2);
42 setLongName(lname);
43 }
44
SkyObject(int t,double r,double d,float m,const QString & n,const QString & n2,const QString & lname)45 SkyObject::SkyObject(int t, double r, double d, float m, const QString &n, const QString &n2, const QString &lname)
46 : SkyPoint(r, d)
47 {
48 setType(t);
49 sortMagnitude = m;
50 setName(n);
51 setName2(n2);
52 setLongName(lname);
53 }
54
clone() const55 SkyObject *SkyObject::clone() const
56 {
57 Q_ASSERT(typeid(this) == typeid(static_cast<const SkyObject *>(this))); // Ensure we are not slicing a derived class
58 return new SkyObject(*this);
59 }
60
showPopupMenu(KSPopupMenu * pmenu,const QPoint & pos)61 void SkyObject::showPopupMenu(KSPopupMenu *pmenu, const QPoint &pos)
62 {
63 #if defined(KSTARS_LITE)
64 Q_UNUSED(pos)
65 Q_UNUSED(pmenu);
66 #else
67 initPopupMenu(pmenu);
68 pmenu->popup(pos);
69 #endif
70 }
71
initPopupMenu(KSPopupMenu * pmenu)72 void SkyObject::initPopupMenu(KSPopupMenu *pmenu)
73 {
74 #ifdef KSTARS_LITE
75 Q_UNUSED(pmenu)
76 #else
77 pmenu->createEmptyMenu(this);
78 #endif
79 }
80
setLongName(const QString & longname)81 void SkyObject::setLongName(const QString &longname)
82 {
83 if (longname.isEmpty())
84 {
85 if (hasName())
86 LongName = name();
87 else if (hasName2())
88 LongName = name2();
89 else
90 LongName.clear();
91 }
92 else
93 {
94 LongName = longname;
95 }
96 }
97
riseSetTime(const KStarsDateTime & dt,const GeoLocation * geo,bool rst,bool exact) const98 QTime SkyObject::riseSetTime(const KStarsDateTime &dt, const GeoLocation *geo, bool rst, bool exact) const
99 {
100 // If this object does not rise or set, return an invalid time
101 SkyPoint p = recomputeCoords(dt, geo);
102 if (p.checkCircumpolar(geo->lat()))
103 return QTime();
104
105 //First of all, if the object is below the horizon at date/time dt, adjust the time
106 //to bring it above the horizon
107 KStarsDateTime dt2 = dt;
108 dms lst(geo->GSTtoLST(dt.gst()));
109 p.EquatorialToHorizontal(&lst, geo->lat());
110 if (p.alt().Degrees() < 0.0)
111 {
112 if (p.az().Degrees() < 180.0) //object has not risen yet
113 {
114 dt2 = dt.addSecs(12. * 3600.); // Move forward 12 hours, to a time when it has already risen
115 }
116 else //object has already set
117 {
118 dt2 = dt.addSecs(-12. * 3600.); // Move backward 12 hours, to a time when it has not yet set
119 }
120 }
121 // The addition / subtraction of 12 hours ensures that we always
122 // compute the _closest_ rise time and the _closest_ set time to
123 // the current time.
124
125 QTime rstUt = riseSetTimeUT(dt2, geo, rst, exact);
126 if (!rstUt.isValid())
127 return QTime();
128
129 return geo->UTtoLT(KStarsDateTime(dt2.date(), rstUt)).time();
130 }
131
riseSetTimeUT(const KStarsDateTime & dt,const GeoLocation * geo,bool riseT,bool exact) const132 QTime SkyObject::riseSetTimeUT(const KStarsDateTime &dt, const GeoLocation *geo, bool riseT, bool exact) const
133 {
134 // First trial to calculate UT
135 QTime UT = auxRiseSetTimeUT(dt, geo, &ra(), &dec(), riseT);
136
137 // We iterate once more using the calculated UT to compute again
138 // the ra and dec for that time and hence the rise/set time.
139 // Also, adjust the date by +/- 1 day, if necessary
140
141 // By adding this +/- 1 day, we are double-checking that the
142 // reported rise-time is the _already_ (last) risen time, and that
143 // the reported set-time is the _future_ (next) set time
144 //
145 // However, issues with this are taken care of in
146 // SkyObject::riseSetTime()
147
148 KStarsDateTime dt0 = dt;
149 dt0.setTime(UT);
150 if (riseT && dt0 > dt)
151 {
152 dt0 = dt0.addDays(-1);
153 }
154 else if (!riseT && dt0 < dt)
155 {
156 dt0 = dt0.addDays(1);
157 }
158
159 SkyPoint sp = recomputeCoords(dt0, geo);
160 UT = auxRiseSetTimeUT(dt0, geo, &sp.ra(), &sp.dec(), riseT);
161
162 if (exact)
163 {
164 // We iterate a second time (For the Moon the second iteration changes
165 // aprox. 1.5 arcmin the coordinates).
166 dt0.setTime(UT);
167 sp = recomputeCoords(dt0, geo);
168 UT = auxRiseSetTimeUT(dt0, geo, &sp.ra(), &sp.dec(), riseT);
169 }
170
171 return UT;
172 }
173
auxRiseSetTimeUT(const KStarsDateTime & dt,const GeoLocation * geo,const dms * righta,const dms * decl,bool riseT) const174 QTime SkyObject::auxRiseSetTimeUT(const KStarsDateTime &dt, const GeoLocation *geo, const dms *righta, const dms *decl,
175 bool riseT) const
176 {
177 dms LST = auxRiseSetTimeLST(geo->lat(), righta, decl, riseT);
178 return dt.GSTtoUT(geo->LSTtoGST(LST));
179 }
180
auxRiseSetTimeLST(const dms * gLat,const dms * righta,const dms * decl,bool riseT) const181 dms SkyObject::auxRiseSetTimeLST(const dms *gLat, const dms *righta, const dms *decl, bool riseT) const
182 {
183 dms h0 = elevationCorrection();
184 double H = approxHourAngle(&h0, gLat, decl);
185 dms LST;
186
187 if (riseT)
188 LST.setH(24.0 + righta->Hours() - H / 15.0);
189 else
190 LST.setH(righta->Hours() + H / 15.0);
191
192 return LST.reduce();
193 }
194
riseSetTimeAz(const KStarsDateTime & dt,const GeoLocation * geo,bool riseT) const195 dms SkyObject::riseSetTimeAz(const KStarsDateTime &dt, const GeoLocation *geo, bool riseT) const
196 {
197 dms Azimuth;
198 double AltRad, AzRad;
199 double sindec, cosdec, sinlat, coslat, sinHA, cosHA;
200 double sinAlt, cosAlt;
201
202 QTime UT = riseSetTimeUT(dt, geo, riseT);
203 KStarsDateTime dt0 = dt;
204 dt0.setTime(UT);
205 SkyPoint sp = recomputeCoords(dt0, geo);
206
207 dms LST = auxRiseSetTimeLST(geo->lat(), &sp.ra0(), &sp.dec0(), riseT);
208 dms HourAngle = dms(LST.Degrees() - sp.ra0().Degrees());
209
210 geo->lat()->SinCos(sinlat, coslat);
211 dec().SinCos(sindec, cosdec);
212 HourAngle.SinCos(sinHA, cosHA);
213
214 sinAlt = sindec * sinlat + cosdec * coslat * cosHA;
215 AltRad = asin(sinAlt);
216 cosAlt = cos(AltRad);
217
218 AzRad = acos((sindec - sinlat * sinAlt) / (coslat * cosAlt));
219 if (sinHA > 0.0)
220 AzRad = 2.0 * dms::PI - AzRad; // resolve acos() ambiguity
221 Azimuth.setRadians(AzRad);
222
223 return Azimuth;
224 }
225
transitTimeUT(const KStarsDateTime & dt,const GeoLocation * geo) const226 QTime SkyObject::transitTimeUT(const KStarsDateTime &dt, const GeoLocation *geo) const
227 {
228 dms LST = geo->GSTtoLST(dt.gst());
229
230 //dSec is the number of seconds until the object transits.
231 dms HourAngle = dms(LST.Degrees() - ra().Degrees());
232 int dSec = static_cast<int>(-3600. * HourAngle.Degrees() / 15.0);
233
234 //dt0 is the first guess at the transit time.
235 KStarsDateTime dt0 = dt.addSecs(dSec);
236 //recompute object's position at UT0 and then find transit time of this refined position
237 SkyPoint sp = recomputeCoords(dt0, geo);
238 HourAngle = dms(LST.Degrees() - sp.ra().Degrees());
239 dSec = static_cast<int>(-3600. * HourAngle.Degrees() / 15.0);
240
241 return dt.addSecs(dSec).time();
242 }
243
transitTime(const KStarsDateTime & dt,const GeoLocation * geo) const244 QTime SkyObject::transitTime(const KStarsDateTime &dt, const GeoLocation *geo) const
245 {
246 return geo->UTtoLT(KStarsDateTime(dt.date(), transitTimeUT(dt, geo))).time();
247 }
248
transitAltitude(const KStarsDateTime & dt,const GeoLocation * geo) const249 dms SkyObject::transitAltitude(const KStarsDateTime &dt, const GeoLocation *geo) const
250 {
251 KStarsDateTime dt0 = dt;
252 dt0.setTime(transitTimeUT(dt, geo));
253 SkyPoint sp = recomputeCoords(dt0, geo);
254
255 double delta = 90 - geo->lat()->Degrees() + sp.dec().Degrees();
256 if (delta > 90)
257 delta = 180 - delta;
258 return dms(delta);
259 }
260
approxHourAngle(const dms * h0,const dms * gLat,const dms * dec) const261 double SkyObject::approxHourAngle(const dms *h0, const dms *gLat, const dms *dec) const
262 {
263 double sh0 = sin(h0->radians());
264 double r = (sh0 - sin(gLat->radians()) * sin(dec->radians())) / (cos(gLat->radians()) * cos(dec->radians()));
265
266 double H = acos(r) / dms::DegToRad;
267
268 return H;
269 }
270
elevationCorrection(void) const271 dms SkyObject::elevationCorrection(void) const
272 {
273 /* The atmospheric refraction at the horizon shifts altitude by
274 * - 34 arcmin = 0.5667 degrees. This value changes if the observer
275 * is above the horizon, or if the weather conditions change much.
276 *
277 * For the sun we have to add half the angular sie of the body, since
278 * the sunset is the time the upper limb of the sun disappears below
279 * the horizon, and dawn, when the upper part of the limb appears
280 * over the horizon. The angular size of the sun = angular size of the
281 * moon = 31' 59''.
282 *
283 * So for the sun the correction is = -34 - 16 = 50 arcmin = -0.8333
284 *
285 * This same correction should be applied to the moon however parallax
286 * is important here. Meeus states that the correction should be
287 * 0.7275 P - 34 arcmin, where P is the moon's horizontal parallax.
288 * He proposes a mean value of 0.125 degrees if no great accuracy
289 * is needed.
290 */
291
292 if (name() == i18n("Sun") || name() == i18n("Moon") || name() == i18n("Earth Shadow"))
293 return dms(-0.8333);
294 // else if ( name() == "Moon" )
295 // return dms(0.125);
296 else // All sources point-like.
297 return dms(-0.5667);
298 }
299
recomputeCoords(const KStarsDateTime & dt,const GeoLocation * geo) const300 SkyPoint SkyObject::recomputeCoords(const KStarsDateTime &dt, const GeoLocation *geo) const
301 {
302 // Create a clone
303 SkyObject *c = this->clone();
304
305 // compute coords of the copy for new time jd
306 KSNumbers num(dt.djd());
307
308 // Note: isSolarSystem() below should give the same result on this
309 // and c. The only very minor reason to prefer this is so that we
310 // have an additional layer of warnings about subclasses of
311 // KSPlanetBase that do not implement SkyObject::clone() due to
312 // the passing of lat and LST
313
314 if (isSolarSystem() && geo)
315 {
316 CachingDms LST = geo->GSTtoLST(dt.gst());
317 c->updateCoords(&num, true, geo->lat(), &LST);
318 }
319 else
320 {
321 c->updateCoords(&num);
322 }
323
324 // Transfer the coordinates into a SkyPoint
325 SkyPoint p = *c;
326
327 // Delete the clone
328 delete c;
329
330 // Return the SkyPoint
331 return p;
332 }
333
recomputeHorizontalCoords(const KStarsDateTime & dt,const GeoLocation * geo) const334 SkyPoint SkyObject::recomputeHorizontalCoords(const KStarsDateTime &dt, const GeoLocation *geo) const
335 {
336 Q_ASSERT(geo);
337 SkyPoint ret = recomputeCoords(dt, geo);
338 CachingDms LST = geo->GSTtoLST(dt.gst());
339 ret.EquatorialToHorizontal(&LST, geo->lat());
340 return ret;
341 }
342
typeName(int t)343 QString SkyObject::typeName(int t)
344 {
345 switch (t)
346 {
347 case STAR:
348 return i18n("Star");
349 case CATALOG_STAR:
350 return i18n("Catalog Star");
351 case PLANET:
352 return i18n("Planet");
353 case OPEN_CLUSTER:
354 return i18n("Open Cluster");
355 case GLOBULAR_CLUSTER:
356 return i18n("Globular Cluster");
357 case GASEOUS_NEBULA:
358 return i18n("Gaseous Nebula");
359 case PLANETARY_NEBULA:
360 return i18n("Planetary Nebula");
361 case SUPERNOVA_REMNANT:
362 return i18n("Supernova Remnant");
363 case GALAXY:
364 return i18n("Galaxy");
365 case COMET:
366 return i18n("Comet");
367 case ASTEROID:
368 return i18n("Asteroid");
369 case CONSTELLATION:
370 return i18n("Constellation");
371 case MOON:
372 return i18n("Moon");
373 case GALAXY_CLUSTER:
374 return i18n("Galaxy Cluster");
375 case SATELLITE:
376 return i18n("Satellite");
377 case SUPERNOVA:
378 return i18n("Supernova");
379 case RADIO_SOURCE:
380 return i18n("Radio Source");
381 case ASTERISM:
382 return i18n("Asterism");
383 case DARK_NEBULA:
384 return i18n("Dark Nebula");
385 case QUASAR:
386 return i18n("Quasar");
387 case MULT_STAR:
388 return i18n("Multiple Star");
389 default:
390 return i18n("Unknown Type");
391 }
392 }
393
typeName() const394 QString SkyObject::typeName() const
395 {
396 return typeName(Type);
397 }
398
messageFromTitle(const QString & imageTitle) const399 QString SkyObject::messageFromTitle(const QString &imageTitle) const
400 {
401 QString message = imageTitle;
402
403 //HST Image
404 if (imageTitle == i18n("Show HST Image") || imageTitle.contains("HST"))
405 {
406 message = i18n("%1: Hubble Space Telescope, operated by STScI for NASA [public domain]", longname());
407
408 //Spitzer Image
409 }
410 else if (imageTitle.contains(i18n("Show Spitzer Image")))
411 {
412 message = i18n("%1: Spitzer Space Telescope, courtesy NASA/JPL-Caltech [public domain]", longname());
413
414 //SEDS Image
415 }
416 else if (imageTitle == i18n("Show SEDS Image"))
417 {
418 message = i18n("%1: SEDS, http://www.seds.org [free for non-commercial use]", longname());
419
420 //Kitt Peak AOP Image
421 }
422 else if (imageTitle == i18n("Show KPNO AOP Image"))
423 {
424 message = i18n("%1: Advanced Observing Program at Kitt Peak National Observatory [free for non-commercial use; "
425 "no physical reproductions]",
426 longname());
427
428 //NOAO Image
429 }
430 else if (imageTitle.contains(i18n("Show NOAO Image")))
431 {
432 message =
433 i18n("%1: National Optical Astronomy Observatories and AURA [free for non-commercial use]", longname());
434
435 //VLT Image
436 }
437 else if (imageTitle.contains("VLT"))
438 {
439 message = i18n("%1: Very Large Telescope, operated by the European Southern Observatory [free for "
440 "non-commercial use; no reproductions]",
441 longname());
442
443 //All others
444 }
445 else if (imageTitle.startsWith(i18n("Show")))
446 {
447 message = imageTitle.mid(imageTitle.indexOf(" ") + 1); //eat first word, "Show"
448 message = longname() + ": " + message;
449 }
450
451 return message;
452 }
453
labelString() const454 QString SkyObject::labelString() const
455 {
456 return translatedName();
457 }
458
labelOffset() const459 double SkyObject::labelOffset() const
460 {
461 return SkyLabeler::ZoomOffset();
462 }
463
getUID() const464 SkyObject::UID SkyObject::getUID() const
465 {
466 return invalidUID;
467 }
468