File indexing completed on 2025-01-12 09:34:26
0001 /* 0002 SPDX-FileCopyrightText: 2016 Jasem Mutlaq <mutlaqja@ikarustech.com> 0003 0004 Based on Samikshan Bairagya GSoC work. 0005 0006 SPDX-License-Identifier: GPL-2.0-or-later 0007 */ 0008 0009 #include "supernovaecomponent.h" 0010 0011 #ifndef KSTARS_LITE 0012 #include "kstars.h" 0013 #include "skymap.h" 0014 #else 0015 #include "kstarslite.h" 0016 #endif 0017 #include "dms.h" 0018 #include "kstars_debug.h" 0019 #include "ksnotification.h" 0020 #include "kstarsdata.h" 0021 #include "Options.h" 0022 #include "skylabeler.h" 0023 #include "skymesh.h" 0024 #include "skypainter.h" 0025 #include "auxiliary/filedownloader.h" 0026 #include "projections/projector.h" 0027 #include "auxiliary/kspaths.h" 0028 0029 #include <QtConcurrent> 0030 #include <QJsonDocument> 0031 #include <QJsonValue> 0032 0033 #include <zlib.h> 0034 #include <fstream> 0035 #include <stdio.h> 0036 0037 #include <csv.h> 0038 0039 const QString SupernovaeComponent::tnsDataFilename("tns_public_objects.csv"); 0040 const QString SupernovaeComponent::tnsDataFilenameZip("tns-daily.csv.gz"); 0041 const QString SupernovaeComponent::tnsDataUrl( 0042 "https://indilib.org/jdownloads/kstars/tns-daily.csv.gz"); 0043 0044 SupernovaeComponent::SupernovaeComponent(SkyComposite *parent) : ListComponent(parent) 0045 { 0046 //QtConcurrent::run(this, &SupernovaeComponent::loadData); 0047 //loadData(); MagnitudeLimitShowSupernovae 0048 connect(Options::self(), &Options::SupernovaDownloadUrlChanged, this, 0049 &SupernovaeComponent::loadData); 0050 connect(Options::self(), &Options::MagnitudeLimitShowSupernovaeChanged, this, 0051 &SupernovaeComponent::loadData); 0052 } 0053 0054 void SupernovaeComponent::update(KSNumbers *num) 0055 { 0056 if (!selected() || !m_DataLoaded) 0057 return; 0058 0059 KStarsData *data = KStarsData::Instance(); 0060 for (auto so : m_ObjectList) 0061 { 0062 if (num) 0063 so->updateCoords(num); 0064 so->EquatorialToHorizontal(data->lst(), data->geo()->lat()); 0065 } 0066 } 0067 0068 bool SupernovaeComponent::selected() 0069 { 0070 return Options::showSupernovae(); 0071 } 0072 0073 void SupernovaeComponent::loadData() 0074 { 0075 qDeleteAll(m_ObjectList); 0076 m_ObjectList.clear(); 0077 0078 objectNames(SkyObject::SUPERNOVA).clear(); 0079 objectLists(SkyObject::SUPERNOVA).clear(); 0080 0081 auto sFileName = KSPaths::locate(QStandardPaths::AppLocalDataLocation, QString(tnsDataFilename)); 0082 0083 try 0084 { 0085 io::CSVReader<26, io::trim_chars<' '>, io::double_quote_escape<',', '\"'>, 0086 io::ignore_overflow> 0087 in(sFileName.toLocal8Bit()); 0088 // skip header 0089 const char *line = in.next_line(); 0090 if (line == nullptr) 0091 { 0092 qCritical() << "file is empty\n"; 0093 return; 0094 } 0095 0096 std::string id, name, ra_s, dec_s, type; 0097 double redshift; 0098 std::string host_name; 0099 double host_redshift; 0100 std::string reporting_group, disc_datasource, classifying_group, assoc_group, 0101 disc_internal_name, disc_instrument, classifying_instrument; 0102 int tns_at, is_public; 0103 std::string end_prop_period; 0104 double discovery_mag; 0105 std::string discovery_filter, discovery_date_s, sender, remarks, 0106 discovery_bibcode, classification_bibcode, ext_catalog; 0107 0108 while (in.read_row( 0109 id, name, ra_s, dec_s, type, redshift, host_name, host_redshift, 0110 reporting_group, disc_datasource, classifying_group, assoc_group, 0111 disc_internal_name, disc_instrument, classifying_instrument, tns_at, 0112 is_public, end_prop_period, discovery_mag, discovery_filter, discovery_date_s, 0113 sender, remarks, discovery_bibcode, classification_bibcode, ext_catalog)) 0114 { 0115 auto discovery_date = 0116 QDateTime::fromString(discovery_date_s.c_str(), Qt::ISODate); 0117 QString qname = QString(name.c_str()); 0118 dms ra(QString(ra_s.c_str()), false); 0119 dms dec(QString(dec_s.c_str()), true); 0120 0121 Supernova *sup = new Supernova( 0122 qname, ra, dec, QString(type.c_str()), QString(host_name.c_str()), 0123 QString(discovery_date_s.c_str()), redshift, discovery_mag, discovery_date); 0124 0125 objectNames(SkyObject::SUPERNOVA).append(QString(name.c_str())); 0126 0127 appendListObject(sup); 0128 objectLists(SkyObject::SUPERNOVA) 0129 .append(QPair<QString, const SkyObject *>(qname, sup)); 0130 } 0131 0132 m_DataLoading = false; 0133 m_DataLoaded = true; 0134 } 0135 catch (io::error::can_not_open_file &ex) 0136 { 0137 qCCritical(KSTARS) << "could not open file " << sFileName.toLocal8Bit() << "\n"; 0138 return; 0139 } 0140 catch (std::exception &ex) 0141 { 0142 qCCritical(KSTARS) << "unknown exception happened:" << ex.what() << "\n"; 0143 } 0144 } 0145 0146 SkyObject *SupernovaeComponent::objectNearest(SkyPoint *p, double &maxrad) 0147 { 0148 if (!selected() || !m_DataLoaded) 0149 return nullptr; 0150 0151 SkyObject *oBest = nullptr; 0152 double rBest = maxrad; 0153 0154 for (auto &so : m_ObjectList) 0155 { 0156 double r = so->angularDistanceTo(p).Degrees(); 0157 //qDebug()<<r; 0158 if (r < rBest) 0159 { 0160 oBest = so; 0161 rBest = r; 0162 } 0163 } 0164 maxrad = rBest; 0165 return oBest; 0166 } 0167 0168 float SupernovaeComponent::zoomMagnitudeLimit() 0169 { 0170 //adjust maglimit for ZoomLevel 0171 double lgmin = log10(MINZOOM); 0172 double lgz = log10(Options::zoomFactor()); 0173 0174 return 14.0 + 2.222 * (lgz - lgmin) + 0175 2.222 * log10(static_cast<double>(Options::starDensity())); 0176 } 0177 0178 void SupernovaeComponent::draw(SkyPainter *skyp) 0179 { 0180 //qDebug()<<"selected()="<<selected(); 0181 if (!selected()) 0182 return; 0183 else if (!m_DataLoaded) 0184 { 0185 if (!m_DataLoading) 0186 { 0187 m_DataLoading = true; 0188 QtConcurrent::run(this, &SupernovaeComponent::loadData); 0189 } 0190 return; 0191 } 0192 0193 double maglim = zoomMagnitudeLimit(); 0194 double refage = Options::supernovaDetectionAge(); 0195 bool hostOnly = Options::supernovaeHostOnly(); 0196 bool classifiedOnly = Options::supernovaeClassifiedOnly(); 0197 0198 for (auto so : m_ObjectList) 0199 { 0200 Supernova *sup = dynamic_cast<Supernova *>(so); 0201 float mag = sup->mag(); 0202 float age = sup->getAgeDays(); 0203 QString type = sup->getType(); 0204 0205 if (mag > float(Options::magnitudeLimitShowSupernovae())) 0206 continue; 0207 0208 if (age > refage) 0209 continue; 0210 0211 // only SN with host galaxy? 0212 if (hostOnly && sup->getHostGalaxy() == "") 0213 continue; 0214 0215 // Do not draw if mag>maglim 0216 if (mag > maglim && Options::limitSupernovaeByZoom()) 0217 continue; 0218 0219 // classified SN only? 0220 if (classifiedOnly && type == "") 0221 continue; 0222 0223 skyp->drawSupernova(sup); 0224 } 0225 } 0226 0227 #if 0 0228 void SupernovaeComponent::notifyNewSupernovae() 0229 { 0230 #ifndef KSTARS_LITE 0231 //qDebug()<<"New Supernovae discovered"; 0232 QList<SkyObject *> latestList; 0233 foreach (SkyObject * so, latest) 0234 { 0235 Supernova * sup = (Supernova *)so; 0236 0237 if (sup->getMagnitude() > float(Options::magnitudeLimitAlertSupernovae())) 0238 { 0239 //qDebug()<<"Not Bright enough to be notified"; 0240 continue; 0241 } 0242 0243 //qDebug()<<"Bright enough to be notified"; 0244 latestList.append(so); 0245 } 0246 if (!latestList.empty()) 0247 { 0248 NotifyUpdatesUI * ui = new NotifyUpdatesUI(0); 0249 ui->addItems(latestList); 0250 ui->show(); 0251 } 0252 // if (!latest.empty()) 0253 // KMessageBox::informationList(0, i18n("New Supernovae discovered!"), latestList, i18n("New Supernovae discovered!")); 0254 #endif 0255 } 0256 #endif 0257 0258 void SupernovaeComponent::slotTriggerDataFileUpdate() 0259 { 0260 delete (downloadJob); 0261 downloadJob = new FileDownloader(); 0262 auto shownames = Options::showSupernovaNames(); 0263 auto age = Options::supernovaDetectionAge(); 0264 QString url = Options::supernovaDownloadUrl(); 0265 QString output = QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)) 0266 .filePath(tnsDataFilenameZip); 0267 0268 if (!url.startsWith("file://")) 0269 { 0270 qInfo() << "fetching data from web: " << url << "\n"; 0271 downloadJob->setProgressDialogEnabled(true, i18n("Supernovae Update"), 0272 i18n("Downloading Supernovae updates...")); 0273 0274 QObject::connect(downloadJob, SIGNAL(downloaded()), this, SLOT(downloadReady())); 0275 QObject::connect(downloadJob, SIGNAL(error(QString)), this, 0276 SLOT(downloadError(QString))); 0277 0278 downloadJob->setDownloadedFileURL(QUrl::fromLocalFile(output)); 0279 downloadJob->get(QUrl(url)); 0280 } 0281 else 0282 { 0283 // if we have a local file as url 0284 // copy data manually to target location 0285 QString fname = url.right(url.size() - 7); 0286 qInfo() << "fetching data from local file at: " << fname << "\n"; 0287 QFile::remove(fname); 0288 auto res = QFile::copy(fname, output); 0289 qInfo() << "copy returned: " << res << "\n"; 0290 // uncompress csv 0291 unzipData(); 0292 // Reload Supernova 0293 loadData(); 0294 } 0295 } 0296 0297 void SupernovaeComponent::unzipData() 0298 { 0299 // TODO: error handling 0300 std::string ifpath = 0301 QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)) 0302 .filePath(tnsDataFilenameZip) 0303 .toStdString(); 0304 std::string ofpath = 0305 QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)) 0306 .filePath(tnsDataFilename) 0307 .toStdString(); 0308 auto fhz = gzopen(ifpath.c_str(), "rb"); 0309 auto fout = fopen(ofpath.c_str(), "wb"); 0310 0311 if (fhz == NULL) 0312 { 0313 printf("Error: Failed to gzopen %s\n", ifpath.c_str()); 0314 exit(0); 0315 } 0316 unsigned char unzipBuffer[8192]; 0317 unsigned int unzippedBytes; 0318 while (true) 0319 { 0320 unzippedBytes = gzread(fhz, unzipBuffer, 8192); 0321 if (unzippedBytes > 0) 0322 { 0323 fwrite(unzipBuffer, 1, unzippedBytes, fout); 0324 } 0325 else 0326 { 0327 break; 0328 } 0329 } 0330 gzclose(fhz); 0331 fclose(fout); 0332 } 0333 0334 void SupernovaeComponent::downloadReady() 0335 { 0336 // uncompress csv 0337 unzipData(); 0338 // Reload Supernova 0339 loadData(); 0340 #ifdef KSTARS_LITE 0341 KStarsLite::Instance()->data()->setFullTimeUpdate(); 0342 #else 0343 KStars::Instance()->data()->setFullTimeUpdate(); 0344 #endif 0345 downloadJob->deleteLater(); 0346 } 0347 0348 void SupernovaeComponent::downloadError(const QString &errorString) 0349 { 0350 KSNotification::error(i18n("Error downloading supernova data: %1", errorString)); 0351 downloadJob->deleteLater(); 0352 }