File indexing completed on 2024-04-28 15:11:24

0001 /*
0002     SPDX-FileCopyrightText: 2012 Samikshan Bairagya <samikshan@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "skyobjitem.h"
0008 
0009 #include "catalogobject.h"
0010 #include "ksfilereader.h"
0011 #include "kspaths.h"
0012 #include "ksplanetbase.h"
0013 #include "kstarsdata.h"
0014 #include "ksutils.h"
0015 
0016 #include <QDirIterator>
0017 
0018 SkyObjItem::SkyObjItem(SkyObject *so)
0019     : m_Name(so->name()), m_LongName(so->longname()), m_TypeName(so->typeName()), m_So(so)
0020 {
0021     switch (so->type())
0022     {
0023         case SkyObject::PLANET:
0024         case SkyObject::MOON:
0025             m_Type = Planet;
0026             break;
0027         case SkyObject::STAR:
0028         case SkyObject::CATALOG_STAR:
0029         case SkyObject::MULT_STAR:
0030             m_Type = Star;
0031             break;
0032         case SkyObject::CONSTELLATION:
0033         case SkyObject::ASTERISM:
0034             m_Type = Constellation;
0035             break;
0036         case SkyObject::GALAXY:
0037             m_Type = Galaxy;
0038             break;
0039         case SkyObject::OPEN_CLUSTER:
0040         case SkyObject::GLOBULAR_CLUSTER:
0041         case SkyObject::GALAXY_CLUSTER:
0042             m_Type = Cluster;
0043             break;
0044         case SkyObject::PLANETARY_NEBULA:
0045         case SkyObject::SUPERNOVA_REMNANT:
0046         case SkyObject::GASEOUS_NEBULA:
0047         case SkyObject::DARK_NEBULA:
0048             m_Type = Nebula;
0049             break;
0050         case SkyObject::SUPERNOVA:
0051             m_Type = Supernova;
0052     }
0053 
0054     setPosition(m_So);
0055 }
0056 
0057 QVariant SkyObjItem::data(int role)
0058 {
0059     switch (role)
0060     {
0061         case DispNameRole:
0062             return getDescName();
0063         case DispImageRole:
0064             return getImageURL(true);
0065         case DispSummaryRole:
0066             return getSummary(true);
0067         case CategoryRole:
0068             return getType();
0069         case CategoryNameRole:
0070             return getTypeName();
0071         default:
0072             return QVariant();
0073     }
0074 }
0075 
0076 ///Moved to skyobjlistmodel.cpp
0077 /*
0078 QHash<int, QByteArray> SkyObjItem::roleNames() const
0079 {
0080     QHash<int, QByteArray> roles;
0081     roles[DispNameRole] = "dispName";
0082     roles[CategoryRole] = "type";
0083     roles[CategoryNameRole] = "typeName";
0084     return roles;
0085 }
0086 */
0087 
0088 void SkyObjItem::setPosition(SkyObject *so)
0089 {
0090     double altitude;
0091     dms azimuth;
0092     if (so->type() == SkyObject::SATELLITE)
0093     {
0094         altitude = so->alt().Degrees();
0095         azimuth  = so->az();
0096     }
0097     else
0098     {
0099         KStarsData *data  = KStarsData::Instance();
0100         KStarsDateTime ut = data->geo()->LTtoUT(
0101             KStarsDateTime(QDateTime::currentDateTime().toLocalTime()));
0102         SkyPoint sp = so->recomputeCoords(ut, data->geo());
0103 
0104         //check altitude of object at this time.
0105         sp.EquatorialToHorizontal(data->lst(), data->geo()->lat());
0106         altitude = sp.alt().Degrees();
0107         azimuth  = sp.az();
0108     }
0109 
0110     double rounded_altitude = (int)(altitude / 5.0) * 5.0;
0111 
0112     if (rounded_altitude <= 0)
0113         m_Position = "<span style='color:red'>" +
0114                      xi18n("NOT VISIBLE: About %1 degrees below the %2 horizon",
0115                            -rounded_altitude, KSUtils::toDirectionString(azimuth)) +
0116                      "</span>";
0117     else
0118         m_Position = "<span style='color:yellow'>" +
0119                      xi18n("Now visible: About %1 degrees above the %2 horizon",
0120                            rounded_altitude, KSUtils::toDirectionString(azimuth)) +
0121                      "</span>";
0122 }
0123 
0124 QString findImage(const QString &prefix, const SkyObject &obj, const QString &suffix)
0125 {
0126     static const auto base =
0127         KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
0128     QDirIterator search(
0129         base,
0130         QStringList() << prefix + obj.name().toLower().remove(' ').remove('/') + suffix,
0131         QDir::Files, QDirIterator::Subdirectories);
0132 
0133     return search.hasNext() ? QUrl::fromLocalFile(search.next()).url() : "";
0134 }
0135 QString SkyObjItem::getImageURL(bool preferThumb) const
0136 {
0137     const auto &thumbURL = findImage("thumb-", *m_So, ".png");
0138 
0139     const auto &fullSizeURL = findImage("image-", *m_So, ".png");
0140     const auto &wikiImageURL =
0141         QUrl::fromLocalFile(KSPaths::locate(QStandardPaths::AppLocalDataLocation,
0142                                             "descriptions/wikiImage-" +
0143                                                 m_So->name().toLower().remove(' ') +
0144                                                 ".png"))
0145             .url();
0146     QString XPlanetURL =
0147         QUrl::fromLocalFile(KSPaths::locate(QStandardPaths::AppLocalDataLocation,
0148                                             "xplanet/" + m_So->name() + ".png"))
0149             .url();
0150 
0151     //First try to return the preferred file
0152     if (!thumbURL.isEmpty() && preferThumb)
0153         return thumbURL;
0154     if (!fullSizeURL.isEmpty() && (!preferThumb))
0155         return fullSizeURL;
0156 
0157     //If that fails, try to return the large image first, then the thumb, and then if it is a planet, the xplanet image. Finally if all else fails, the wiki image.
0158     QString fname = fullSizeURL;
0159 
0160     if (fname.isEmpty())
0161     {
0162         fname = thumbURL;
0163     }
0164     if (fname.isEmpty() && m_Type == Planet)
0165     {
0166         fname = XPlanetURL;
0167     }
0168     if (fname.isEmpty())
0169     {
0170         fname = wikiImageURL;
0171     }
0172     return fname;
0173 }
0174 
0175 QString SkyObjItem::getSummary(bool includeDescription) const
0176 {
0177     if (includeDescription)
0178     {
0179         QString description = loadObjectDescription();
0180         if(description.indexOf(".") > 0) //This will shorten the description in the list to just a sentence, whereas in the larger space of the Object Information Summary, it is a full paragraph.
0181            return m_So->typeName() + "<BR>" + getRADE() + "<BR>" + getAltAz() + "<BR><BR>" + description.left(description.indexOf(".") + 1);
0182         else
0183             return m_So->typeName() + "<BR>" + getRADE() + "<BR>" + getAltAz() + "<BR><BR>" + description;
0184     }
0185     else
0186         return m_So->typeName() + "<BR>" + getRADE() + "<BR>" + getAltAz();
0187 }
0188 
0189 QString SkyObjItem::getSurfaceBrightness() const
0190 {
0191     /** Surface Brightness is applicable only for extended light sources like
0192       * Deep-Sky Objects. Here we use the formula SB = m + log10(a*b/4)
0193       * where m is the magnitude of the sky-object. a and b are the major and minor
0194       * axis lengths of the objects respectively in arcminutes. SB is the surface
0195       * brightness obtained in mag * arcminutes^-2
0196       */
0197 
0198     auto *dso = dynamic_cast<CatalogObject*>(m_So);
0199     float SB           = m_So->mag();
0200 
0201     if (dso != nullptr)
0202         SB += 2.5 * log10(dso->a() * dso->b() / 4);
0203 
0204     switch (getType())
0205     {
0206         case Galaxy:
0207         case Nebula:
0208             return QLocale().toString(SB, 'f', 2) + "<BR>   (mag/arcmin^2)";
0209         default:
0210             return QString(" --"); // Not applicable for other sky-objects
0211     }
0212 }
0213 
0214 QString SkyObjItem::getSize() const
0215 {
0216     switch (getType())
0217     {
0218         case Galaxy:
0219         case Cluster:
0220         case Nebula:
0221             return QLocale().toString(((CatalogObject *)m_So)->a(), 'f', 2) + "\"";
0222         case Planet:
0223             return QLocale().toString(((KSPlanetBase *)m_So)->angSize(), 'f', 2) + "\"";
0224         default:
0225             return QString(" --");
0226     }
0227 }
0228 
0229 inline QString SkyObjItem::loadObjectDescription() const
0230 {
0231     QFile file;
0232     QString fname = "description-" + getName().toLower().remove(' ') + ".html";
0233     //determine filename in local user KDE directory tree.
0234     file.setFileName(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)).filePath("descriptions/" + fname));
0235 
0236     if (file.exists())
0237     {
0238         if (file.open(QIODevice::ReadOnly))
0239         {
0240             QTextStream in(&file);
0241             QString line;
0242             line = in.readLine(); //This should only read the description since the source is on the next line
0243             file.close();
0244             return line;
0245         }
0246     }
0247     return getTypeName();
0248 }
0249 
0250 QString SkyObjItem::getRADE() const
0251 {
0252     return "RA: " + m_So->ra().toHMSString() + "<BR>DE: " + m_So->dec().toDMSString();
0253 }
0254 
0255 QString SkyObjItem::getAltAz() const
0256 {
0257     return "Alt: " + QString::number(m_So->alt().Degrees(), 'f', 2) +
0258            ", Az: " + QString::number(m_So->az().Degrees(), 'f', 2);
0259 }
0260 
0261 float SkyObjItem::getMagnitude() const
0262 {
0263     return m_So->mag();
0264 }