File indexing completed on 2024-03-24 15:17:04

0001 /*
0002     SPDX-FileCopyrightText: 2021 Jasem Mutlaq
0003 
0004     Static version of the HIPS Renderer for a single point in the sky.
0005 
0006     SPDX-License-Identifier: GPL-2.0-or-later
0007 */
0008 
0009 #include "hipsfinder.h"
0010 
0011 #include "kstars_debug.h"
0012 #include "Options.h"
0013 #include "kspaths.h"
0014 #include "projections/projector.h"
0015 #include "projections/lambertprojector.h"
0016 
0017 ///////////////////////////////////////////////////////////////////////////////////////////
0018 ///
0019 ///////////////////////////////////////////////////////////////////////////////////////////
0020 HIPSFinder * HIPSFinder::m_Instance = nullptr;
0021 
0022 ///////////////////////////////////////////////////////////////////////////////////////////
0023 ///
0024 ///////////////////////////////////////////////////////////////////////////////////////////
0025 HIPSFinder *HIPSFinder::Instance()
0026 {
0027     if (m_Instance == nullptr)
0028     {
0029         m_Instance = new HIPSFinder();
0030     }
0031 
0032     return m_Instance;
0033 }
0034 
0035 ///////////////////////////////////////////////////////////////////////////////////////////
0036 ///
0037 ///////////////////////////////////////////////////////////////////////////////////////////
0038 HIPSFinder::HIPSFinder()
0039 {
0040     m_ScanRender.reset(new ScanRender());
0041     m_HEALpix.reset(new HEALPix());
0042 }
0043 
0044 ///////////////////////////////////////////////////////////////////////////////////////////
0045 /// Static
0046 ///////////////////////////////////////////////////////////////////////////////////////////
0047 bool HIPSFinder::render(SkyPoint *center, uint8_t level, double zoom, QImage *destinationImage, double &fov_w,
0048                         double &fov_h)
0049 {
0050     double ra = center->ra0().radians();
0051     double de = center->dec0().radians();
0052     // do we need this? or updateCoords?
0053     //center.catalogueCoord(KStarsData::Instance()->updateNum()->julianDay());
0054 
0055     if (std::isnan(ra) || std::isnan(de))
0056     {
0057         qCWarning(KSTARS) << "NAN Center, HiPS rendering failed.";
0058         return false;
0059     }
0060 
0061     m_RenderedMap.clear();
0062 
0063     // Setup sample projector
0064     ViewParams viewParams;
0065     viewParams.width = destinationImage->width();
0066     viewParams.height = destinationImage->height();
0067     viewParams.fillGround = false;
0068     viewParams.useAltAz = false;
0069     viewParams.zoomFactor = zoom;
0070     viewParams.focus = center;
0071 
0072     m_Projector.reset(new LambertProjector(viewParams));
0073 
0074     // Get the ID of the face at this level containing the coordinates.
0075     int centerPix = m_HEALpix->getPix(level, ra, de);
0076 
0077     SkyPoint cornerSkyCoords[4];
0078     QPointF tileLine[2];
0079 
0080     // Get corners for this face
0081     m_HEALpix->getCornerPoints(level, centerPix, cornerSkyCoords);
0082 
0083     fov_w = cornerSkyCoords[0].angularDistanceTo(&cornerSkyCoords[1]).Degrees();
0084     fov_h = cornerSkyCoords[1].angularDistanceTo(&cornerSkyCoords[2]).Degrees();
0085 
0086     // Map the tile lines to the corners
0087     for (int i = 0; i < 2; i++)
0088         tileLine[i] = m_Projector->toScreen(&cornerSkyCoords[i]);
0089 
0090     int size = std::sqrt(std::pow(tileLine[0].x() - tileLine[1].x(), 2) + std::pow(tileLine[0].y() - tileLine[1].y(), 2));
0091     if (size < 0)
0092         size = HIPSManager::Instance()->getCurrentTileWidth();
0093 
0094     m_ScanRender->setBilinearInterpolationEnabled(size >= HIPSManager::Instance()->getCurrentTileWidth());
0095 
0096     renderRec(level, centerPix, destinationImage);
0097 
0098     return !m_RenderedMap.isEmpty();
0099 }
0100 
0101 ///////////////////////////////////////////////////////////////////////////////////////////
0102 /// Static
0103 ///////////////////////////////////////////////////////////////////////////////////////////
0104 bool HIPSFinder::renderFOV(SkyPoint *center, double fov_radius, double rotation, QImage *destinationImage)
0105 {
0106     double ra = center->ra0().radians();
0107     double de = center->dec0().radians();
0108     // do we need this? or updateCoords?
0109     //center.catalogueCoord(KStarsData::Instance()->updateNum()->julianDay());
0110 
0111     if (std::isnan(ra) || std::isnan(de))
0112     {
0113         qCWarning(KSTARS) << "NAN Center, HiPS rendering failed.";
0114         return false;
0115     }
0116 
0117     m_RenderedMap.clear();
0118 
0119     auto width = destinationImage->width();
0120     auto height = destinationImage->height();
0121     auto zoom = sqrt(width * width + height * height) / (fov_radius * 2 * M_PI / 180.0);
0122 
0123     // Setup sample projector
0124     ViewParams viewParams;
0125     viewParams.width = width;
0126     viewParams.height = height;
0127     viewParams.fillGround = false;
0128     viewParams.useAltAz = false;
0129     viewParams.zoomFactor = zoom;
0130     viewParams.rotationAngle = dms(rotation);
0131     viewParams.focus = center;
0132 
0133     m_Projector.reset(new LambertProjector(viewParams));
0134 
0135     uint8_t level = 1;
0136 
0137     // Min FOV in Degrees
0138     double minfov = 58.5;
0139     double fov    = m_Projector->fov() * width / height;
0140 
0141     // Find suitable level for current FOV
0142     while(fov < minfov)
0143     {
0144         minfov /= 2;
0145         level++;
0146     }
0147 
0148     // We need this in case of offline storage missing a few levels.
0149     level = HIPSManager::Instance()->getUsableOfflineLevel(level);
0150 
0151     // Get the ID of the face at this level containing the coordinates.
0152     int centerPix = m_HEALpix->getPix(level, ra, de);
0153 
0154     m_ScanRender->setBilinearInterpolationEnabled(Options::hIPSBiLinearInterpolation());
0155 
0156     renderRec(level, centerPix, destinationImage);
0157 
0158     return !m_RenderedMap.isEmpty();
0159 }
0160 
0161 
0162 ///////////////////////////////////////////////////////////////////////////////////////////
0163 /// Static
0164 ///////////////////////////////////////////////////////////////////////////////////////////
0165 void HIPSFinder::renderRec(uint8_t level, int pix, QImage *destinationImage)
0166 {
0167     if (m_RenderedMap.contains(pix))
0168         return;
0169 
0170     if (renderPix(level, pix, destinationImage))
0171     {
0172         m_RenderedMap.insert(pix);
0173         int dirs[8];
0174         int nside = 1 << level;
0175 
0176         m_HEALpix->neighbours(nside, pix, dirs);
0177 
0178         renderRec(level, dirs[0], destinationImage);
0179         renderRec(level, dirs[2], destinationImage);
0180         renderRec(level, dirs[4], destinationImage);
0181         renderRec(level, dirs[6], destinationImage);
0182     }
0183 }
0184 
0185 ///////////////////////////////////////////////////////////////////////////////////////////
0186 ///
0187 ///////////////////////////////////////////////////////////////////////////////////////////
0188 bool HIPSFinder::renderPix(int level, int pix, QImage *destinationImage)
0189 {
0190     SkyPoint cornerSkyCoords[4];
0191     QPointF cornerScreenCoords[4];
0192     bool isVisible = false;
0193 
0194     m_HEALpix->getCornerPoints(level, pix, cornerSkyCoords);
0195 
0196     for (int i = 0; i < 4; i++)
0197     {
0198         cornerScreenCoords[i] = m_Projector->toScreen(&cornerSkyCoords[i]);
0199         isVisible |= m_Projector->checkVisibility(&cornerSkyCoords[i]);
0200     }
0201 
0202     if (isVisible)
0203     {
0204         int dir = (pix / 10000) * 10000;
0205         QString path = KSPaths::locate(QStandardPaths::AppLocalDataLocation,
0206                                        QString("/HIPS/Norder%1/Dir%2/Npix%3.jpg").arg(level).arg(dir).arg(pix));
0207         QImage sourceImage(path);
0208 
0209         if (!sourceImage.isNull())
0210         {
0211             QPointF uv[16][4] = {{QPointF(.25, .25), QPointF(0.25, 0), QPointF(0, .0), QPointF(0, .25)},
0212                 {QPointF(.25, .5), QPointF(0.25, 0.25), QPointF(0, .25), QPointF(0, .5)},
0213                 {QPointF(.5, .25), QPointF(0.5, 0), QPointF(.25, .0), QPointF(.25, .25)},
0214                 {QPointF(.5, .5), QPointF(0.5, 0.25), QPointF(.25, .25), QPointF(.25, .5)},
0215 
0216                 {QPointF(.25, .75), QPointF(0.25, 0.5), QPointF(0, 0.5), QPointF(0, .75)},
0217                 {QPointF(.25, 1), QPointF(0.25, 0.75), QPointF(0, .75), QPointF(0, 1)},
0218                 {QPointF(.5, .75), QPointF(0.5, 0.5), QPointF(.25, .5), QPointF(.25, .75)},
0219                 {QPointF(.5, 1), QPointF(0.5, 0.75), QPointF(.25, .75), QPointF(.25, 1)},
0220 
0221                 {QPointF(.75, .25), QPointF(0.75, 0), QPointF(0.5, .0), QPointF(0.5, .25)},
0222                 {QPointF(.75, .5), QPointF(0.75, 0.25), QPointF(0.5, .25), QPointF(0.5, .5)},
0223                 {QPointF(1, .25), QPointF(1, 0), QPointF(.75, .0), QPointF(.75, .25)},
0224                 {QPointF(1, .5), QPointF(1, 0.25), QPointF(.75, .25), QPointF(.75, .5)},
0225 
0226                 {QPointF(.75, .75), QPointF(0.75, 0.5), QPointF(0.5, .5), QPointF(0.5, .75)},
0227                 {QPointF(.75, 1), QPointF(0.75, 0.75), QPointF(0.5, .75), QPointF(0.5, 1)},
0228                 {QPointF(1, .75), QPointF(1, 0.5), QPointF(.75, .5), QPointF(.75, .75)},
0229                 {QPointF(1, 1), QPointF(1, 0.75), QPointF(.75, .75), QPointF(.75, 1)},
0230             };
0231 
0232             int childPixelID[4];
0233 
0234             // Find all the 4 children of the current pixel
0235             m_HEALpix->getPixChilds(pix, childPixelID);
0236 
0237             int j = 0;
0238             for (int id : childPixelID)
0239             {
0240                 int grandChildPixelID[4];
0241                 // Find the children of this child (i.e. grand child)
0242                 // Then we have 4x4 pixels under the primary pixel
0243                 // The image is interpolated and rendered over these pixels
0244                 // coordinate to minimize any distortions due to the projection
0245                 // system.
0246                 m_HEALpix->getPixChilds(id, grandChildPixelID);
0247 
0248                 QPointF fineScreenCoords[4];
0249 
0250                 for (int id2 : grandChildPixelID)
0251                 {
0252                     SkyPoint fineSkyPoints[4];
0253                     m_HEALpix->getCornerPoints(level + 2, id2, fineSkyPoints);
0254 
0255                     for (int i = 0; i < 4; i++)
0256                         fineScreenCoords[i] = m_Projector->toScreen(&fineSkyPoints[i]);
0257 
0258                     m_ScanRender->renderPolygon(3, fineScreenCoords, destinationImage, &sourceImage, uv[j]);
0259                     j++;
0260                 }
0261             }
0262 
0263             return true;
0264         }
0265     }
0266 
0267     return false;
0268 }