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 }