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

0001 /*
0002     SPDX-FileCopyrightText: 2005 Jason Harris <kstars@30doradus.org>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "constellationboundarylines.h"
0008 
0009 #include "ksfilereader.h"
0010 #include "kstarsdata.h"
0011 #include "linelist.h"
0012 #include "Options.h"
0013 #include "polylist.h"
0014 #ifdef KSTARS_LITE
0015 #include "skymaplite.h"
0016 #else
0017 #include "skymap.h"
0018 #endif
0019 #include "skypainter.h"
0020 #include "htmesh/MeshIterator.h"
0021 #include "skycomponents/skymapcomposite.h"
0022 
0023 #include <QHash>
0024 
0025 ConstellationBoundaryLines::ConstellationBoundaryLines(SkyComposite *parent)
0026     : NoPrecessIndex(parent, i18n("Constellation Boundaries"))
0027 {
0028     m_skyMesh      = SkyMesh::Instance();
0029     m_polyIndexCnt = 0;
0030     for (int i = 0; i < m_skyMesh->size(); i++)
0031     {
0032         m_polyIndex.append(std::shared_ptr<PolyListList>(new PolyListList()));
0033     }
0034 
0035     KStarsData *data = KStarsData::Instance();
0036     int verbose      = 0; // -1 => create cbounds-$x.idx on stdout
0037     //  0 => normal
0038     const char *fname = "cbounds.dat";
0039     int flag = 0;
0040     double ra, dec = 0, lastRa, lastDec;
0041     std::shared_ptr<LineList> lineList;
0042     std::shared_ptr<PolyList> polyList;
0043     bool ok = false;
0044 
0045     intro();
0046 
0047     // Open the .idx file and skip past the first line
0048     KSFileReader idxReader, *idxFile = nullptr;
0049     QString idxFname = QString("cbounds-%1.idx").arg(SkyMesh::Instance()->level());
0050     if (idxReader.open(idxFname))
0051     {
0052         idxReader.readLine();
0053         idxFile = &idxReader;
0054     }
0055 
0056     // now open the file that contains the points
0057     KSFileReader fileReader;
0058     if (!fileReader.open(fname))
0059         return;
0060 
0061     fileReader.setProgress(i18n("Loading Constellation Boundaries"), 13124, 10);
0062 
0063     lastRa = lastDec = -1000.0;
0064 
0065     while (fileReader.hasMoreLines())
0066     {
0067         QString line = fileReader.readLine();
0068         fileReader.showProgress();
0069 
0070         if (line.at(0) == '#')
0071             continue;          // ignore comments
0072         if (line.at(0) == ':') // :constellation line
0073         {
0074             if (lineList.get())
0075                 appendLine(lineList);
0076             lineList.reset();
0077 
0078             if (polyList.get())
0079                 appendPoly(polyList, idxFile, verbose);
0080             QString cName = line.mid(1);
0081             polyList.reset(new PolyList(cName));
0082             if (verbose == -1)
0083                 printf(":\n");
0084             lastRa = lastDec = -1000.0;
0085             continue;
0086         }
0087 
0088         // read in the data from the line
0089         ra = line.midRef(0, 12).toDouble(&ok);
0090         if (ok)
0091             dec = line.midRef(13, 12).toDouble(&ok);
0092         if (ok)
0093             flag = line.midRef(26, 1).toInt(&ok);
0094         if (!ok)
0095         {
0096             fprintf(stderr, "%s: conversion error on line: %d\n", fname, fileReader.lineNumber());
0097             continue;
0098         }
0099 
0100         if (ra == lastRa && dec == lastDec)
0101         {
0102             fprintf(stderr, "%s: tossing dupe on line %4d: (%f, %f)\n", fname, fileReader.lineNumber(), ra, dec);
0103             continue;
0104         }
0105 
0106         // always add the point to the boundary (and toss dupes)
0107 
0108         // By the time we come here, we should have polyList. Else we aren't doing good
0109         Q_ASSERT(polyList); // Is this the right fix?
0110 
0111         polyList->append(QPointF(ra, dec));
0112         if (ra < 0)
0113             polyList->setWrapRA(true);
0114 
0115         if (flag)
0116         {
0117             if (!lineList.get())
0118                 lineList.reset(new LineList());
0119 
0120             std::shared_ptr<SkyPoint> point(new SkyPoint(ra, dec));
0121 
0122             point->EquatorialToHorizontal(data->lst(), data->geo()->lat());
0123             lineList->append(std::move(point));
0124             lastRa  = ra;
0125             lastDec = dec;
0126         }
0127         else
0128         {
0129             if (lineList.get())
0130                 appendLine(lineList);
0131             lineList.reset();
0132             lastRa = lastDec = -1000.0;
0133         }
0134     }
0135 
0136     if (lineList.get())
0137         appendLine(lineList);
0138     if (polyList.get())
0139         appendPoly(polyList, idxFile, verbose);
0140 }
0141 
0142 bool ConstellationBoundaryLines::selected()
0143 {
0144 #ifndef KSTARS_LITE
0145     return Options::showCBounds() && !(Options::hideOnSlew() && Options::hideCBounds() && SkyMap::IsSlewing());
0146 #else
0147     return Options::showCBounds() && !(Options::hideOnSlew() && Options::hideCBounds() && SkyMapLite::IsSlewing());
0148 #endif
0149 }
0150 
0151 void ConstellationBoundaryLines::preDraw(SkyPainter *skyp)
0152 {
0153     QColor color = KStarsData::Instance()->colorScheme()->colorNamed("CBoundColor");
0154     skyp->setPen(QPen(QBrush(color), 1, Qt::SolidLine));
0155 }
0156 
0157 void ConstellationBoundaryLines::appendPoly(std::shared_ptr<PolyList> &polyList, KSFileReader *file, int debug)
0158 {
0159     if (!file || debug == -1)
0160         return appendPoly(polyList, debug);
0161 
0162     while (file->hasMoreLines())
0163     {
0164         QString line = file->readLine();
0165         if (line.at(0) == ':')
0166             return;
0167         Trixel trixel = line.toInt();
0168 
0169         m_polyIndex[trixel]->append(polyList);
0170     }
0171 }
0172 
0173 void ConstellationBoundaryLines::appendPoly(const std::shared_ptr<PolyList> &polyList, int debug)
0174 {
0175     if (debug >= 0 && debug < m_skyMesh->debug())
0176         debug = m_skyMesh->debug();
0177 
0178     const IndexHash &indexHash     = m_skyMesh->indexPoly(polyList->poly());
0179     IndexHash::const_iterator iter = indexHash.constBegin();
0180     while (iter != indexHash.constEnd())
0181     {
0182         Trixel trixel = iter.key();
0183         iter++;
0184 
0185         if (debug == -1)
0186             printf("%d\n", trixel);
0187 
0188         m_polyIndex[trixel]->append(polyList);
0189     }
0190 
0191     if (debug > 9)
0192         printf("PolyList: %3d: %d\n", ++m_polyIndexCnt, indexHash.size());
0193 }
0194 
0195 PolyList *ConstellationBoundaryLines::ContainingPoly(const SkyPoint *p) const
0196 {
0197     //printf("called ContainingPoly(p)\n");
0198 
0199     // we save the pointers in a hash because most often there is only one
0200     // constellation and we can avoid doing the expensive boundary calculations
0201     // and just return it if we know it is unique.  We can avoid this minor
0202     // complication entirely if we use index(p) instead of aperture(p, r)
0203     // because index(p) always returns a single trixel index.
0204 
0205     QHash<PolyList *, bool> polyHash;
0206     QHash<PolyList *, bool>::const_iterator iter;
0207 
0208     //printf("\n");
0209 
0210     // the boundaries don't precess so we use index() not aperture()
0211     m_skyMesh->index(p, 1.0, IN_CONSTELL_BUF);
0212     MeshIterator region(m_skyMesh, IN_CONSTELL_BUF);
0213     while (region.hasNext())
0214     {
0215         Trixel trixel = region.next();
0216         //printf("Trixel: %4d %s\n", trixel, m_skyMesh->indexToName( trixel ) );
0217 
0218         std::shared_ptr<PolyListList> polyListList = m_polyIndex[trixel];
0219 
0220         //printf("    size: %d\n", polyListList->size() );
0221 
0222         for (const auto &item : *polyListList)
0223         {
0224             polyHash.insert(item.get(), true);
0225         }
0226     }
0227 
0228     iter = polyHash.constBegin();
0229 
0230     // Don't bother with boundaries if there is only one
0231     if (polyHash.size() == 1)
0232         return iter.key();
0233 
0234     QPointF point(p->ra().Hours(), p->dec().Degrees());
0235     QPointF wrapPoint(p->ra().Hours() - 24.0, p->dec().Degrees());
0236     bool wrapRA = p->ra().Hours() > 12.0;
0237 
0238     while (iter != polyHash.constEnd())
0239     {
0240         PolyList *polyList = iter.key();
0241         iter++;
0242 
0243         //qDebug() << Q_FUNC_INFO << QString("checking %1 boundary\n").arg( polyList->name() );
0244 
0245         const QPolygonF *poly = polyList->poly();
0246         if (wrapRA && polyList->wrapRA())
0247         {
0248             if (poly->containsPoint(wrapPoint, Qt::OddEvenFill))
0249                 return polyList;
0250         }
0251         else
0252         {
0253             if (poly->containsPoint(point, Qt::OddEvenFill))
0254                 return polyList;
0255         }
0256     }
0257 
0258     return nullptr;
0259 }
0260 
0261 //-------------------------------------------------------------------
0262 // The routines for providing public access to the boundary index
0263 // start here.  (Some of them may not be needed (or working)).
0264 //-------------------------------------------------------------------
0265 
0266 QString ConstellationBoundaryLines::constellationName(const SkyPoint *p) const
0267 {
0268     PolyList *polyList = ContainingPoly(p);
0269     if (polyList)
0270     {
0271         return (Options::useLocalConstellNames() ?
0272                     i18nc("Constellation name (optional)", polyList->name().toUpper().toLocal8Bit().data()) :
0273                     polyList->name());
0274     }
0275     return i18n("Unknown");
0276 }