File indexing completed on 2024-04-21 14:46:43

0001 /*
0002     SPDX-FileCopyrightText: 2001 Jason Harris <jharris@30doradus.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "ksplanetbase.h"
0008 
0009 #include "ksnumbers.h"
0010 #include "kstarsdata.h"
0011 #include "ksutils.h"
0012 #include "Options.h"
0013 #include "skymap.h"
0014 #include "ksasteroid.h"
0015 #include "kscomet.h"
0016 #include "ksmoon.h"
0017 #include "ksplanet.h"
0018 #include "kssun.h"
0019 #include "texturemanager.h"
0020 #include "skycomponents/skymapcomposite.h"
0021 
0022 QVector<QColor> KSPlanetBase::planetColor = QVector<QColor>() << QColor("slateblue") << //Mercury
0023         QColor("lightgreen") <<                     //Venus
0024         QColor("red") <<                            //Mars
0025         QColor("goldenrod") <<                      //Jupiter
0026         QColor("khaki") <<                          //Saturn
0027         QColor("lightseagreen") <<                  //Uranus
0028         QColor("skyblue") <<                        //Neptune
0029         QColor("grey") <<                           //Pluto
0030         QColor("yellow") <<                         //Sun
0031         QColor("white");                            //Moon
0032 
0033 const SkyObject::UID KSPlanetBase::UID_SOL_BIGOBJ   = 0;
0034 const SkyObject::UID KSPlanetBase::UID_SOL_ASTEROID = 1;
0035 const SkyObject::UID KSPlanetBase::UID_SOL_COMET    = 2;
0036 
0037 KSPlanetBase::KSPlanetBase(const QString &s, const QString &image_file, const QColor &c, double pSize)
0038     : TrailObject(2, 0.0, 0.0, 0.0, s)
0039 {
0040     init(s, image_file, c, pSize);
0041 }
0042 
0043 void KSPlanetBase::init(const QString &s, const QString &image_file, const QColor &c, double pSize)
0044 {
0045     m_image       = TextureManager::getImage(image_file);
0046     PositionAngle = 0.0;
0047     PhysicalSize  = pSize;
0048     m_Color       = c;
0049     setName(s);
0050     setLongName(s);
0051 }
0052 
0053 KSPlanetBase *KSPlanetBase::createPlanet(int n)
0054 {
0055     switch (n)
0056     {
0057         case KSPlanetBase::MERCURY:
0058         case KSPlanetBase::VENUS:
0059         case KSPlanetBase::MARS:
0060         case KSPlanetBase::JUPITER:
0061         case KSPlanetBase::SATURN:
0062         case KSPlanetBase::URANUS:
0063         case KSPlanetBase::NEPTUNE:
0064             return new KSPlanet(n);
0065         /*case KSPlanetBase::PLUTO:
0066             return new KSPluto();
0067             break;*/
0068         case KSPlanetBase::SUN:
0069             return new KSSun();
0070         case KSPlanetBase::MOON:
0071             return new KSMoon();
0072     }
0073     return nullptr;
0074 }
0075 
0076 void KSPlanetBase::EquatorialToEcliptic(const CachingDms *Obliquity)
0077 {
0078     findEcliptic(Obliquity, ep.longitude, ep.latitude);
0079 }
0080 
0081 void KSPlanetBase::EclipticToEquatorial(const CachingDms *Obliquity)
0082 {
0083     setFromEcliptic(Obliquity, ep.longitude, ep.latitude);
0084 }
0085 
0086 void KSPlanetBase::updateCoords(const KSNumbers *num, bool includePlanets, const CachingDms *lat, const CachingDms *LST,
0087                                 bool)
0088 {
0089     KStarsData *kd = KStarsData::Instance();
0090 
0091     if (kd == nullptr || !includePlanets)
0092         return;
0093 
0094     kd->skyComposite()->earth()->findPosition(num); //since we don't pass lat & LST, localizeCoords will be skipped
0095 
0096     if (lat && LST)
0097     {
0098         findPosition(num, lat, LST, kd->skyComposite()->earth());
0099         // Don't add to the trail this time
0100         if (hasTrail())
0101             Trail.takeLast();
0102     }
0103     else
0104     {
0105         findGeocentricPosition(num, kd->skyComposite()->earth());
0106     }
0107 }
0108 
0109 void KSPlanetBase::findPosition(const KSNumbers *num, const CachingDms *lat, const CachingDms *LST,
0110                                 const KSPlanetBase *Earth)
0111 {
0112 
0113     lastPrecessJD = num->julianDay();
0114 
0115     findGeocentricPosition(num, Earth); //private function, reimplemented in each subclass
0116     findPhase();
0117     setAngularSize(findAngularSize()); //angular size in arcmin
0118 
0119     if (lat && LST)
0120         localizeCoords(num, lat, LST); //correct for figure-of-the-Earth
0121 
0122     if (hasTrail())
0123     {
0124         addToTrail(KStarsDateTime(num->getJD()).toString("yyyy.MM.dd hh:mm") +
0125                    i18nc("Universal time", "UT")); // TODO: Localize date/time format?
0126         if (Trail.size() > TrailObject::MaxTrail)
0127             clipTrail();
0128     }
0129 
0130     findMagnitude(num);
0131 
0132     if (type() == SkyObject::COMET)
0133     {
0134         // Compute tail size
0135         KSComet *me = static_cast<KSComet *>(this);
0136         double comaAngSize;
0137         // Convert the tail size in km to angular tail size (degrees)
0138         comaAngSize = asin(physicalSize() / Rearth / AU_KM) * 60.0 * 180.0 / dms::PI;
0139         // Find the apparent length as projected on the celestial sphere (the comet's tail points away from the sun)
0140         me->setComaAngSize(comaAngSize * fabs(sin(phase().radians())));
0141     }
0142 }
0143 
0144 bool KSPlanetBase::isMajorPlanet() const
0145 {
0146     if (name() == i18n("Mercury") || name() == i18n("Venus") || name() == i18n("Mars") || name() == i18n("Jupiter") ||
0147             name() == i18n("Saturn") || name() == i18n("Uranus") || name() == i18n("Neptune"))
0148         return true;
0149     return false;
0150 }
0151 
0152 void KSPlanetBase::localizeCoords(const KSNumbers *num, const CachingDms *lat, const CachingDms *LST)
0153 {
0154     //convert geocentric coordinates to local apparent coordinates (topocentric coordinates)
0155     dms HA, HA2; //Hour Angle, before and after correction
0156     double rsinp, rcosp, u, sinHA, cosHA, sinDec, cosDec, D;
0157     double cosHA2;
0158     double r = Rearth * AU_KM; //distance from Earth, in km
0159     u        = atan(0.996647 * tan(lat->radians()));
0160     rsinp    = 0.996647 * sin(u);
0161     rcosp    = cos(u);
0162     HA.setD(LST->Degrees() - ra().Degrees());
0163     HA.SinCos(sinHA, cosHA);
0164     dec().SinCos(sinDec, cosDec);
0165 
0166     D = atan2(rcosp * sinHA, r * cosDec / 6378.14 - rcosp * cosHA);
0167     dms temp;
0168     temp.setRadians(ra().radians() - D);
0169     setRA(temp);
0170 
0171     HA2.setD(LST->Degrees() - ra().Degrees());
0172     cosHA2 = cos(HA2.radians());
0173 
0174     //temp.setRadians( atan2( cosHA2*( r*sinDec/6378.14 - rsinp ), r*cosDec*cosHA/6378.14 - rcosp ) );
0175     // The atan2() version above makes the planets move crazy in the htm branch -jbb
0176     temp.setRadians(atan(cosHA2 * (r * sinDec / 6378.14 - rsinp) / (r * cosDec * cosHA / 6378.14 - rcosp)));
0177 
0178     setDec(temp);
0179 
0180     //Make sure Dec is between -90 and +90
0181     if (dec().Degrees() > 90.0)
0182     {
0183         setDec(180.0 - dec().Degrees());
0184         setRA(ra().Hours() + 12.0);
0185         ra().reduce();
0186     }
0187     if (dec().Degrees() < -90.0)
0188     {
0189         setDec(180.0 + dec().Degrees());
0190         setRA(ra().Hours() + 12.0);
0191         ra().reduce();
0192     }
0193 
0194     EquatorialToEcliptic(num->obliquity());
0195 }
0196 
0197 void KSPlanetBase::setRearth(const KSPlanetBase *Earth)
0198 {
0199     double sinL, sinB, sinL0, sinB0;
0200     double cosL, cosB, cosL0, cosB0;
0201     double x, y, z;
0202 
0203     //The Moon's Rearth is set in its findGeocentricPosition()...
0204     if (name() == i18n("Moon"))
0205     {
0206         return;
0207     }
0208 
0209     if (name() == i18n("Earth"))
0210     {
0211         Rearth = 0.0;
0212         return;
0213     }
0214 
0215     if (!Earth)
0216     {
0217         qDebug() << Q_FUNC_INFO << "KSPlanetBase::setRearth():  Error: Need an Earth pointer.  (" << name() << ")";
0218         Rearth = 1.0;
0219         return;
0220     }
0221 
0222     Earth->ecLong().SinCos(sinL0, cosL0);
0223     Earth->ecLat().SinCos(sinB0, cosB0);
0224     double eX = Earth->rsun() * cosB0 * cosL0;
0225     double eY = Earth->rsun() * cosB0 * sinL0;
0226     double eZ = Earth->rsun() * sinB0;
0227 
0228     helEcLong().SinCos(sinL, cosL);
0229     helEcLat().SinCos(sinB, cosB);
0230     x = rsun() * cosB * cosL - eX;
0231     y = rsun() * cosB * sinL - eY;
0232     z = rsun() * sinB - eZ;
0233 
0234     Rearth = sqrt(x * x + y * y + z * z);
0235 
0236     //Set angular size, in arcmin
0237     AngularSize = asin(PhysicalSize / Rearth / AU_KM) * 60. * 180. / dms::PI;
0238 }
0239 
0240 void KSPlanetBase::findPA(const KSNumbers *num)
0241 {
0242     //Determine position angle of planet (assuming that it is aligned with
0243     //the Ecliptic, which is only roughly correct).
0244     //Displace a point along +Ecliptic Latitude by 1 degree
0245     SkyPoint test;
0246     dms newELat(ecLat().Degrees() + 1.0);
0247     test.setFromEcliptic(num->obliquity(), ecLong(), newELat);
0248     double dx = ra().Degrees() - test.ra().Degrees();
0249     double dy = test.dec().Degrees() - dec().Degrees();
0250     double pa;
0251     if (dy)
0252     {
0253         pa = atan2(dx, dy) * 180.0 / dms::PI;
0254     }
0255     else
0256     {
0257         pa = dx < 0 ? 90.0 : -90.0;
0258     }
0259     setPA(pa);
0260 }
0261 
0262 double KSPlanetBase::labelOffset() const
0263 {
0264     double size = angSize() * dms::PI * Options::zoomFactor() / 10800.0;
0265 
0266     //Determine minimum size for offset
0267     double minsize = 4.;
0268     if (type() == SkyObject::ASTEROID || type() == SkyObject::COMET)
0269         minsize = 2.;
0270     if (name() == i18n("Sun") || name() == i18n("Moon"))
0271         minsize = 8.;
0272     if (size < minsize)
0273         size = minsize;
0274 
0275     //Inflate offset for Saturn
0276     if (name() == i18n("Saturn"))
0277         size = int(2.5 * size);
0278 
0279     return 0.5 * size + 4.;
0280 }
0281 
0282 void KSPlanetBase::findPhase()
0283 {
0284     if (2 * rsun()*rearth() == 0)
0285     {
0286         Phase = std::numeric_limits<double>::quiet_NaN();
0287         return;
0288     }
0289     /* Compute the phase of the planet in degrees */
0290     double earthSun = KStarsData::Instance()->skyComposite()->earth()->rsun();
0291     double cosPhase = (rsun() * rsun() + rearth() * rearth() - earthSun * earthSun) / (2 * rsun() * rearth());
0292 
0293     Phase           = acos(cosPhase) * 180.0 / dms::PI;
0294     /* More elegant way of doing it, but requires the Sun.
0295        TODO: Switch to this if and when we make KSSun a singleton */
0296     //    Phase = ecLong()->Degrees() - Sun->ecLong()->Degrees();
0297 }