File indexing completed on 2025-01-19 09:45:57
0001 /* 0002 SPDX-FileCopyrightText: 2005 Jason Harris <kstars@30doradus.org> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "cometscomponent.h" 0008 0009 #ifndef KSTARS_LITE 0010 #include "kstars.h" 0011 #endif 0012 #include "ksfilereader.h" 0013 #include "kspaths.h" 0014 #include "kstarsdata.h" 0015 #include "ksutils.h" 0016 #include "ksnotification.h" 0017 #include "kstars_debug.h" 0018 #ifndef KSTARS_LITE 0019 #include "skymap.h" 0020 #else 0021 #include "kstarslite.h" 0022 #endif 0023 #include "Options.h" 0024 #include "skylabeler.h" 0025 #include "skypainter.h" 0026 #include "solarsystemcomposite.h" 0027 #include "auxiliary/filedownloader.h" 0028 #include "auxiliary/kspaths.h" 0029 #include "projections/projector.h" 0030 #include "skyobjects/kscomet.h" 0031 0032 #include <QFile> 0033 #include <QHttpMultiPart> 0034 #include <QPen> 0035 #include <QStandardPaths> 0036 0037 #include <cmath> 0038 0039 CometsComponent::CometsComponent(SolarSystemComposite *parent) 0040 : SolarSystemListComponent(parent) 0041 { 0042 loadData(); 0043 } 0044 0045 bool CometsComponent::selected() 0046 { 0047 return Options::showComets(); 0048 } 0049 0050 /* 0051 * @short Initialize the comets list. 0052 * Reads in the comets data from the comets.dat file. 0053 * 0054 * Populate the list of Comets from the data file. 0055 * The data file is a CSV file with the following columns : 0056 * @li 1 full name [string] 0057 * @li 2 modified julian day of orbital elements [int] 0058 * @li 3 perihelion distance in AU [double] 0059 * @li 4 eccentricity of orbit [double] 0060 * @li 5 inclination angle of orbit in degrees [double] 0061 * @li 6 argument of perihelion in degrees [double] 0062 * @li 7 longitude of the ascending node in degrees [double] 0063 * @li 8 time of perihelion passage (YYYYMMDD.DDD) [double] 0064 * @li 9 orbit solution ID [string] 0065 * @li 10 Near-Earth Object (NEO) flag [bool] 0066 * @li 11 comet total magnitude parameter [float] 0067 * @li 12 comet nuclear magnitude parameter [float] 0068 * @li 13 object diameter (from equivalent sphere) [float] 0069 * @li 14 object bi/tri-axial ellipsoid dimensions [string] 0070 * @li 15 geometric albedo [float] 0071 * @li 16 rotation period [float] 0072 * @li 17 orbital period [float] 0073 * @li 18 earth minimum orbit intersection distance [double] 0074 * @li 19 orbit classification [string] 0075 * @li 20 comet total magnitude slope parameter 0076 * @li 21 comet nuclear magnitude slope parameter 0077 * @note See KSComet constructor for more details. 0078 */ 0079 void CometsComponent::loadData() 0080 { 0081 QString name, orbit_class; 0082 0083 emitProgressText(i18n("Loading comets")); 0084 qCInfo(KSTARS) << "Loading comets"; 0085 0086 qDeleteAll(m_ObjectList); 0087 m_ObjectList.clear(); 0088 0089 objectNames(SkyObject::COMET).clear(); 0090 objectLists(SkyObject::COMET).clear(); 0091 0092 QString file_name = KSPaths::locate(QStandardPaths::AppLocalDataLocation, QString("cometels.json.gz")); 0093 0094 try 0095 { 0096 KSUtils::MPCParser com_parser(file_name); 0097 com_parser.for_each( 0098 [&](const auto & get) 0099 { 0100 KSComet *com = nullptr; 0101 name = get("Designation_and_name").toString(); 0102 0103 int perihelion_year, perihelion_month, perihelion_day, perihelion_hour, perihelion_minute, perihelion_second; 0104 0105 // Perihelion Distance in AU 0106 double perihelion_distance = get("Perihelion_dist").toDouble(); 0107 // Orbital Eccentricity 0108 double eccentricity = get("e").toDouble(); 0109 // Argument of perihelion, J2000.0 (degrees) 0110 double perihelion_argument = get("Peri").toDouble(); 0111 // Longitude of the ascending node, J2000.0 (degrees) 0112 double ascending_node = get("Node").toDouble(); 0113 // Inclination in degrees, J2000.0 (degrees) 0114 double inclination = get("i").toDouble(); 0115 0116 // Perihelion Date 0117 perihelion_year = get("Year_of_perihelion").toInt(); 0118 perihelion_month = get("Month_of_perihelion").toInt(); 0119 // Stored as double in MPC 0120 double peri_day = get("Day_of_perihelion").toDouble(); 0121 perihelion_day = static_cast<int>(peri_day); 0122 double peri_hour = (peri_day - perihelion_day) * 24; 0123 perihelion_hour = static_cast<int>(peri_hour); 0124 perihelion_minute = static_cast<int>((peri_hour - perihelion_hour) * 60); 0125 perihelion_second = ( (( peri_hour - perihelion_hour) * 60) - perihelion_minute) * 60; 0126 0127 long double Tp = KStarsDateTime(QDate(perihelion_year, perihelion_month, perihelion_day), 0128 QTime(perihelion_hour, perihelion_minute, perihelion_second)).djd(); 0129 0130 // Orbit type 0131 orbit_class = get("Orbit_type").toString(); 0132 double absolute_magnitude = get("H").toDouble(); 0133 double slope_parameter = get("G").toDouble(); 0134 0135 com = new KSComet(name, 0136 QString(), 0137 perihelion_distance, 0138 eccentricity, 0139 dms(inclination), 0140 dms(perihelion_argument), 0141 dms(ascending_node), 0142 Tp, 0143 absolute_magnitude, 0144 101.0, 0145 slope_parameter, 0146 101.0); 0147 0148 com->setOrbitClass(orbit_class); 0149 com->setAngularSize(0.005); 0150 appendListObject(com); 0151 0152 // Add *short* name to the list of object names 0153 objectNames(SkyObject::COMET).append(com->name()); 0154 objectLists(SkyObject::COMET).append(QPair<QString, const SkyObject *>(com->name(), com)); 0155 }); 0156 } 0157 catch (const std::runtime_error&) 0158 { 0159 qCInfo(KSTARS) << "Loading comets failed."; 0160 qCInfo(KSTARS) << " -> was trying to read " + file_name; 0161 return; 0162 } 0163 } 0164 0165 // Used for JPL Data 0166 // DO NOT REMOVE, we can revert to JPL at any time. 0167 //void CometsComponent::loadData() 0168 //{ 0169 // QString name, orbit_id, orbit_class, dimensions; 0170 0171 // emitProgressText(i18n("Loading comets")); 0172 // qCInfo(KSTARS) << "Loading comets"; 0173 0174 // qDeleteAll(m_ObjectList); 0175 // m_ObjectList.clear(); 0176 0177 // objectNames(SkyObject::COMET).clear(); 0178 // objectLists(SkyObject::COMET).clear(); 0179 0180 // QString file_name = 0181 // KSPaths::locate(QStandardPaths::AppLocalDataLocation, QString("comets.dat")); 0182 0183 // try 0184 // { 0185 // KSUtils::JPLParser com_parser(file_name); 0186 // com_parser.for_each( 0187 // [&](const auto &get) 0188 // { 0189 // KSComet *com = nullptr; 0190 // name = get("full_name").toString(); 0191 // name = name.trimmed(); 0192 // bool neo; 0193 // double q, e, dble_i, dble_w, dble_N, Tp, earth_moid; 0194 // float M1, M2, K1, K2, diameter, albedo, rot_period, period; 0195 // q = get("q").toString().toDouble(); 0196 // e = get("e").toString().toDouble(); 0197 // dble_i = get("i").toString().toDouble(); 0198 // dble_w = get("w").toString().toDouble(); 0199 // dble_N = get("om").toString().toDouble(); 0200 // Tp = get("tp").toString().toDouble(); 0201 // orbit_id = get("orbit_id").toString(); 0202 // neo = get("neo").toString() == "Y"; 0203 0204 // if (get("M1").toString().toFloat() == 0.0) 0205 // M1 = 101.0; 0206 // else 0207 // M1 = get("M1").toString().toFloat(); 0208 0209 // if (get("M2").toString().toFloat() == 0.0) 0210 // M2 = 101.0; 0211 // else 0212 // M2 = get("M2").toString().toFloat(); 0213 0214 // diameter = get("diameter").toString().toFloat(); 0215 // dimensions = get("extent").toString(); 0216 // albedo = get("albedo").toString().toFloat(); 0217 // rot_period = get("rot_per").toString().toFloat(); 0218 // period = get("per.y").toDouble(); 0219 // earth_moid = get("moid").toString().toDouble(); 0220 // orbit_class = get("class").toString(); 0221 // K1 = get("H").toString().toFloat(); 0222 // K2 = get("G").toString().toFloat(); 0223 0224 // com = new KSComet(name, QString(), q, e, dms(dble_i), dms(dble_w), 0225 // dms(dble_N), Tp, M1, M2, K1, K2); 0226 // com->setOrbitID(orbit_id); 0227 // com->setNEO(neo); 0228 // com->setDiameter(diameter); 0229 // com->setDimensions(dimensions); 0230 // com->setAlbedo(albedo); 0231 // com->setRotationPeriod(rot_period); 0232 // com->setPeriod(period); 0233 // com->setEarthMOID(earth_moid); 0234 // com->setOrbitClass(orbit_class); 0235 // com->setAngularSize(0.005); 0236 // appendListObject(com); 0237 0238 // // Add *short* name to the list of object names 0239 // objectNames(SkyObject::COMET).append(com->name()); 0240 // objectLists(SkyObject::COMET) 0241 // .append(QPair<QString, const SkyObject *>(com->name(), com)); 0242 // }); 0243 // } 0244 // catch (const std::runtime_error &e) 0245 // { 0246 // qCInfo(KSTARS) << "Loading comets failed."; 0247 // qCInfo(KSTARS) << " -> was trying to read " + file_name; 0248 // return; 0249 // } 0250 //} 0251 0252 void CometsComponent::draw(SkyPainter *skyp) 0253 { 0254 Q_UNUSED(skyp) 0255 #ifndef KSTARS_LITE 0256 if (!selected() || Options::zoomFactor() < 1 * MINZOOM) 0257 return; 0258 0259 bool hideLabels = !Options::showCometNames() || (SkyMap::Instance()->isSlewing() && Options::hideLabels()); 0260 double rsunLabelLimit = Options::maxRadCometName(); 0261 0262 //FIXME: Should these be config'able? 0263 skyp->setPen(QPen(QColor("transparent"))); 0264 skyp->setBrush(QBrush(QColor("white"))); 0265 0266 for (auto so : m_ObjectList) 0267 { 0268 KSComet *com = dynamic_cast<KSComet *>(so); 0269 double mag = com->mag(); 0270 if (std::isnan(mag) == 0) 0271 { 0272 bool drawn = skyp->drawComet(com); 0273 if (drawn && !(hideLabels || com->rsun() >= rsunLabelLimit)) 0274 SkyLabeler::AddLabel(com, SkyLabeler::COMET_LABEL); 0275 } 0276 } 0277 #endif 0278 } 0279 0280 // DO NOT REMOVE 0281 //void CometsComponent::updateDataFile(bool isAutoUpdate) 0282 //{ 0283 // delete (downloadJob); 0284 // downloadJob = new FileDownloader(); 0285 0286 // if (isAutoUpdate == false) 0287 // downloadJob->setProgressDialogEnabled(true, i18n("Comets Update"), 0288 // i18n("Downloading comets updates...")); 0289 // downloadJob->registerDataVerification([&](const QByteArray &data) 0290 // { return data.startsWith("{\"signature\""); }); 0291 0292 // connect(downloadJob, SIGNAL(downloaded()), this, SLOT(downloadReady())); 0293 0294 // // For auto-update, we ignore errors 0295 // if (isAutoUpdate == false) 0296 // connect(downloadJob, SIGNAL(error(QString)), this, SLOT(downloadError(QString))); 0297 0298 // QUrl url = QUrl("https://ssd-api.jpl.nasa.gov/sbdb_query.api"); 0299 // QByteArray post_data = KSUtils::getJPLQueryString( 0300 // "c", 0301 // "full_name,epoch.mjd,q,e,i,w,om,tp,orbit_id,neo," 0302 // "M1,M2,diameter,extent,albedo,rot_per,per.y,moid,H,G,class", 0303 // QVector<KSUtils::JPLFilter>{}); 0304 // // FIXME: find out what { "Af", "!=", "D" } used to mean 0305 0306 // downloadJob->post(url, post_data); 0307 //} 0308 0309 void CometsComponent::updateDataFile(bool isAutoUpdate) 0310 { 0311 delete (downloadJob); 0312 downloadJob = new FileDownloader(); 0313 0314 if (isAutoUpdate == false) 0315 downloadJob->setProgressDialogEnabled(true, i18n("Comets Update"), 0316 i18n("Downloading comets updates...")); 0317 0318 connect(downloadJob, SIGNAL(downloaded()), this, SLOT(downloadReady())); 0319 0320 // For auto-update, we ignore errors 0321 if (isAutoUpdate == false) 0322 connect(downloadJob, SIGNAL(error(QString)), this, SLOT(downloadError(QString))); 0323 0324 QUrl url = QUrl("https://www.minorplanetcenter.net/Extended_Files/cometels.json.gz"); 0325 downloadJob->get(url); 0326 } 0327 0328 void CometsComponent::downloadReady() 0329 { 0330 // Comment the first line 0331 QByteArray data = downloadJob->downloadedData(); 0332 0333 // Write data to cometels.json.gz 0334 QFile file(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)) 0335 .filePath("cometels.json.gz")); 0336 if (file.open(QIODevice::WriteOnly)) 0337 { 0338 file.write(data); 0339 file.close(); 0340 } 0341 else 0342 qCWarning(KSTARS) << "Failed writing comet data to" << file.fileName(); 0343 0344 QString focusedComet; 0345 0346 #ifdef KSTARS_LITE 0347 SkyObject *foc = KStarsLite::Instance()->map()->focusObject(); 0348 if (foc && foc->type() == SkyObject::COMET) 0349 { 0350 focusedComet = foc->name(); 0351 KStarsLite::Instance()->map()->setFocusObject(nullptr); 0352 } 0353 #else 0354 SkyObject *foc = KStars::Instance()->map()->focusObject(); 0355 if (foc && foc->type() == SkyObject::COMET) 0356 { 0357 focusedComet = foc->name(); 0358 KStars::Instance()->map()->setFocusObject(nullptr); 0359 } 0360 #endif 0361 0362 // Reload comets 0363 loadData(); 0364 0365 #ifdef KSTARS_LITE 0366 KStarsLite::Instance()->data()->setFullTimeUpdate(); 0367 if (!focusedComet.isEmpty()) 0368 KStarsLite::Instance()->map()->setFocusObject( 0369 KStarsLite::Instance()->data()->objectNamed(focusedComet)); 0370 #else 0371 if (!focusedComet.isEmpty()) 0372 KStars::Instance()->map()->setFocusObject( 0373 KStars::Instance()->data()->objectNamed(focusedComet)); 0374 KStars::Instance()->data()->setFullTimeUpdate(); 0375 #endif 0376 0377 downloadJob->deleteLater(); 0378 } 0379 0380 // DO NOT REMOVE 0381 //void CometsComponent::downloadReady() 0382 //{ 0383 // // Comment the first line 0384 // QByteArray data = downloadJob->downloadedData(); 0385 0386 // // Write data to comets.dat 0387 // QFile file(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation)) 0388 // .filePath("comets.dat")); 0389 // if (file.open(QIODevice::WriteOnly | QIODevice::Truncate | QIODevice::Text)) 0390 // { 0391 // file.write(data); 0392 // file.close(); 0393 // } 0394 // else 0395 // qCWarning(KSTARS) << "Failed writing comet data to" << file.fileName(); 0396 0397 // QString focusedComet; 0398 0399 //#ifdef KSTARS_LITE 0400 // SkyObject *foc = KStarsLite::Instance()->map()->focusObject(); 0401 // if (foc && foc->type() == SkyObject::COMET) 0402 // { 0403 // focusedComet = foc->name(); 0404 // KStarsLite::Instance()->map()->setFocusObject(nullptr); 0405 // } 0406 //#else 0407 // SkyObject *foc = KStars::Instance()->map()->focusObject(); 0408 // if (foc && foc->type() == SkyObject::COMET) 0409 // { 0410 // focusedComet = foc->name(); 0411 // KStars::Instance()->map()->setFocusObject(nullptr); 0412 // } 0413 //#endif 0414 0415 // // Reload comets 0416 // loadData(); 0417 0418 //#ifdef KSTARS_LITE 0419 // KStarsLite::Instance()->data()->setFullTimeUpdate(); 0420 // if (!focusedComet.isEmpty()) 0421 // KStarsLite::Instance()->map()->setFocusObject( 0422 // KStarsLite::Instance()->data()->objectNamed(focusedComet)); 0423 //#else 0424 // if (!focusedComet.isEmpty()) 0425 // KStars::Instance()->map()->setFocusObject( 0426 // KStars::Instance()->data()->objectNamed(focusedComet)); 0427 // KStars::Instance()->data()->setFullTimeUpdate(); 0428 //#endif 0429 0430 // downloadJob->deleteLater(); 0431 //} 0432 0433 void CometsComponent::downloadError(const QString &errorString) 0434 { 0435 KSNotification::error(i18n("Error downloading asteroids data: %1", errorString)); 0436 qCCritical(KSTARS) << errorString; 0437 downloadJob->deleteLater(); 0438 }