File indexing completed on 2024-07-14 07:17:21

0001 /*
0002     SPDX-FileCopyrightText: 2009 Jerome SONRIER <jsid@emor3j.fr.eu.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "flagcomponent.h"
0008 
0009 #include "ksfilereader.h"
0010 #include "kstarsdata.h"
0011 #include "Options.h"
0012 #ifdef KSTARS_LITE
0013 #include "skymaplite.h"
0014 #else
0015 #include "skymap.h"
0016 #endif
0017 #include "skypainter.h"
0018 #include "auxiliary/kspaths.h"
0019 #include "projections/projector.h"
0020 #include "skyobjects/skypoint.h"
0021 
0022 #include <KLocalizedString>
0023 
0024 #include <QDir>
0025 #include <QtMath>
0026 #include <QStandardPaths>
0027 
0028 FlagComponent::FlagComponent(SkyComposite *parent) : PointListComponent(parent)
0029 {
0030     // Add the default flag images to available images list
0031     m_Names.append(i18n("No icon"));
0032     m_Images.append(QImage());
0033     m_Names.append(i18n("Default"));
0034     m_Images.append(QImage(KSPaths::locate(QStandardPaths::AppLocalDataLocation, "defaultflag.gif")));
0035 
0036     QDir appDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation));
0037     appDir.setNameFilters(QStringList() << "flag*");
0038     // Add all other images found in user appdata directory
0039     for (auto &item : appDir.entryList())
0040     {
0041         QString path = appDir.absoluteFilePath(item);
0042         m_Images.append(QImage(path));
0043 
0044         QString fileName = item.replace(QRegExp("\\.[^.]*$"), QString()).replace(QRegExp("^flag"), QString()).replace('_', ' ');
0045 
0046         m_Names.append(fileName);
0047     }
0048 
0049     loadFromFile();
0050 }
0051 
0052 void FlagComponent::draw(SkyPainter *skyp)
0053 {
0054     // Return if flags must not be draw
0055     if (!selected())
0056         return;
0057 
0058     // Return if no images are available
0059     if (m_Names.size() < 1)
0060         return;
0061 
0062     // Draw all flags
0063     skyp->drawFlags();
0064 }
0065 
0066 bool FlagComponent::selected()
0067 {
0068     return Options::showFlags();
0069 }
0070 
0071 void FlagComponent::loadFromFile()
0072 {
0073     bool imageFound = false;
0074     QList<QStringList> flagList;
0075     KStarsData::Instance()->userdb()->GetAllFlags(flagList);
0076 
0077     for (auto &flagEntry : flagList)
0078     {
0079         // Read coordinates
0080         dms r(flagEntry.at(0));
0081         dms d(flagEntry.at(1));
0082 
0083         m_EpochCoords.append(qMakePair(r.Degrees(), d.Degrees()));
0084 
0085         std::shared_ptr<SkyPoint> flagPoint(new SkyPoint(r, d));
0086 
0087         // Convert to JNow
0088         toJ2000(flagPoint.get(), flagEntry.at(2));
0089 
0090         flagPoint->updateCoordsNow(KStarsData::Instance()->updateNum());
0091 
0092         pointList().append(std::move(flagPoint));
0093 
0094         // Read epoch
0095         m_Epoch.append(flagEntry.at(2));
0096 
0097         // Read image name
0098         QString str = flagEntry.at(3);
0099         str         = str.replace('_', ' ');
0100         for (int i = 0; i < m_Names.size(); ++i)
0101         {
0102             if (str == m_Names.at(i))
0103             {
0104                 m_FlagImages.append(i);
0105                 imageFound = true;
0106             }
0107         }
0108 
0109         // If the image specified in db does not exist,
0110         // use the default one
0111         if (!imageFound)
0112             m_FlagImages.append(0);
0113 
0114         imageFound = false;
0115 
0116         // If there is no label, use an empty string, red color and continue.
0117         m_Labels.append(flagEntry.at(4));
0118 
0119         // color label
0120 
0121         QRegExp rxLabelColor("^#[a-fA-F0-9]{6}$");
0122         if (rxLabelColor.exactMatch(flagEntry.at(5)))
0123         {
0124             m_LabelColors.append(QColor(flagEntry.at(5)));
0125         }
0126         else
0127         {
0128             m_LabelColors.append(QColor("red"));
0129         }
0130     }
0131 }
0132 
0133 void FlagComponent::saveToFile()
0134 {
0135     /*
0136     TODO: This is a really bad way of storing things. Adding one flag shouldn't
0137     involve writing a new file/table every time. Needs fixing.
0138     */
0139     KStarsData::Instance()->userdb()->DeleteAllFlags();
0140 
0141     for (int i = 0; i < size(); ++i)
0142     {
0143         KStarsData::Instance()->userdb()->AddFlag(QString::number(epochCoords(i).first),
0144                 QString::number(epochCoords(i).second), epoch(i),
0145                 imageName(i).replace(' ', '_'), label(i), labelColor(i).name());
0146     }
0147 }
0148 
0149 void FlagComponent::add(const SkyPoint &flagPoint, QString epoch, QString image, QString label, QColor labelColor)
0150 {
0151     //JM 2015-02-21: Insert original coords in list and convert skypint to JNow
0152     // JM 2017-02-07: Discard above! We add RAW epoch coordinates to list.
0153     // If not J2000, we convert to J2000
0154     m_EpochCoords.append(qMakePair(flagPoint.ra().Degrees(), flagPoint.dec().Degrees()));
0155 
0156     std::shared_ptr<SkyPoint> newFlagPoint(new SkyPoint(flagPoint.ra(), flagPoint.dec()));
0157 
0158     toJ2000(newFlagPoint.get(), epoch);
0159 
0160     newFlagPoint->updateCoordsNow(KStarsData::Instance()->updateNum());
0161 
0162     pointList().append(std::move(newFlagPoint));
0163     m_Epoch.append(epoch);
0164 
0165     for (int i = 0; i < m_Names.size(); i++)
0166     {
0167         if (image == m_Names.at(i))
0168             m_FlagImages.append(i);
0169     }
0170 
0171     m_Labels.append(label);
0172     m_LabelColors.append(labelColor);
0173 }
0174 
0175 void FlagComponent::remove(int index)
0176 {
0177     // check if flag of required index exists
0178     if (index > pointList().size() - 1)
0179     {
0180         return;
0181     }
0182 
0183     pointList().removeAt(index);
0184     m_EpochCoords.removeAt(index);
0185     m_Epoch.removeAt(index);
0186     m_FlagImages.removeAt(index);
0187     m_Labels.removeAt(index);
0188     m_LabelColors.removeAt(index);
0189 
0190     // request SkyMap update
0191 #ifndef KSTARS_LITE
0192     SkyMap::Instance()->forceUpdate();
0193 #endif
0194 }
0195 
0196 void FlagComponent::updateFlag(int index, const SkyPoint &flagPoint, QString epoch, QString image, QString label,
0197                                QColor labelColor)
0198 {
0199     if (index < 0 || index > pointList().size() - 1)
0200         return;
0201 
0202     std::shared_ptr<SkyPoint> existingFlag = pointList().at(index);
0203 
0204     existingFlag->setRA0(flagPoint.ra());
0205     existingFlag->setDec0(flagPoint.dec());
0206 
0207     // If epoch not J2000, to convert to J2000
0208     toJ2000(existingFlag.get(), epoch);
0209 
0210     existingFlag->updateCoordsNow(KStarsData::Instance()->updateNum());
0211 
0212     m_EpochCoords.replace(index, qMakePair(flagPoint.ra().Degrees(), flagPoint.dec().Degrees()));
0213 
0214     m_Epoch.replace(index, epoch);
0215 
0216     for (int i = 0; i < m_Names.size(); i++)
0217     {
0218         if (image == m_Names.at(i))
0219             m_FlagImages.replace(index, i);
0220     }
0221 
0222     m_Labels.replace(index, label);
0223     m_LabelColors.replace(index, labelColor);
0224 }
0225 
0226 QStringList FlagComponent::getNames()
0227 {
0228     return m_Names;
0229 }
0230 
0231 int FlagComponent::size()
0232 {
0233     return pointList().size();
0234 }
0235 
0236 QString FlagComponent::epoch(int index)
0237 {
0238     if (index > m_Epoch.size() - 1)
0239     {
0240         return QString();
0241     }
0242 
0243     return m_Epoch.at(index);
0244 }
0245 
0246 QString FlagComponent::label(int index)
0247 {
0248     if (index > m_Labels.size() - 1)
0249     {
0250         return QString();
0251     }
0252 
0253     return m_Labels.at(index);
0254 }
0255 
0256 QColor FlagComponent::labelColor(int index)
0257 {
0258     if (index > m_LabelColors.size() - 1)
0259     {
0260         return QColor();
0261     }
0262 
0263     return m_LabelColors.at(index);
0264 }
0265 
0266 QImage FlagComponent::image(int index)
0267 {
0268     if (index > m_FlagImages.size() - 1)
0269     {
0270         return QImage();
0271     }
0272 
0273     if (m_FlagImages.at(index) > m_Images.size() - 1)
0274     {
0275         return QImage();
0276     }
0277 
0278     return m_Images.at(m_FlagImages.at(index));
0279 }
0280 
0281 QString FlagComponent::imageName(int index)
0282 {
0283     if (index > m_FlagImages.size() - 1)
0284     {
0285         return QString();
0286     }
0287 
0288     if (m_FlagImages.at(index) > m_Names.size() - 1)
0289     {
0290         return QString();
0291     }
0292 
0293     return m_Names.at(m_FlagImages.at(index));
0294 }
0295 
0296 QList<QImage> FlagComponent::imageList()
0297 {
0298     return m_Images;
0299 }
0300 
0301 QList<int> FlagComponent::getFlagsNearPix(SkyPoint *point, int pixelRadius)
0302 {
0303 #ifdef KSTARS_LITE
0304     const Projector *proj = SkyMapLite::Instance()->projector();
0305 #else
0306     const Projector *proj = SkyMap::Instance()->projector();
0307 #endif
0308     QPointF pos = proj->toScreen(point);
0309     QList<int> retVal;
0310     int ptr = 0;
0311 
0312     for (auto &cp : pointList())
0313     {
0314         if (std::isnan(cp->ra().Degrees()) || std::isnan(cp->dec().Degrees()))
0315             continue;
0316         cp->EquatorialToHorizontal(KStarsData::Instance()->lst(), KStarsData::Instance()->geo()->lat());
0317         QPointF pos2 = proj->toScreen(cp.get());
0318         int dx       = (pos2 - pos).x();
0319         int dy       = (pos2 - pos).y();
0320 
0321         if (qSqrt(dx * dx + dy * dy) <= pixelRadius)
0322         {
0323             //point is inside pixelRadius circle
0324             retVal.append(ptr);
0325         }
0326 
0327         ptr++;
0328     }
0329 
0330     return retVal;
0331 }
0332 
0333 QImage FlagComponent::imageList(int index)
0334 {
0335     if (index < 0 || index > m_Images.size() - 1)
0336     {
0337         return QImage();
0338     }
0339 
0340     return m_Images.at(index);
0341 }
0342 
0343 void FlagComponent::toJ2000(SkyPoint *p, QString epoch)
0344 {
0345     KStarsDateTime dt;
0346     dt.setFromEpoch(epoch);
0347 
0348     if (dt.djd() == J2000)
0349         return;
0350 
0351     p->catalogueCoord(dt.djd());
0352 
0353     // Store J2000 coords in RA0, DEC0
0354     p->setRA0(p->ra());
0355     p->setDec0(p->dec());
0356 }
0357 
0358 QPair<double, double> FlagComponent::epochCoords(int index)
0359 {
0360     if (index > m_FlagImages.size() - 1)
0361     {
0362         QPair<double, double> coord = qMakePair(0, 0);
0363         return coord;
0364     }
0365 
0366     return m_EpochCoords.at(index);
0367 }
0368 
0369 void FlagComponent::update(KSNumbers *num)
0370 {
0371     if (!selected())
0372         return;
0373     KStarsData *data = KStarsData::Instance();
0374 
0375     for (auto &p : pointList())
0376     {
0377         if (num)
0378             p->updateCoordsNow(num);
0379 
0380         p->EquatorialToHorizontal(data->lst(), data->geo()->lat());
0381     }
0382 }