1 /*
2     SPDX-FileCopyrightText: 2016 Jasem Mutlaq <mutlaqja@ikarustech.com>
3 
4     Based on Samikshan Bairagya GSoC work.
5 
6     SPDX-License-Identifier: GPL-2.0-or-later
7 */
8 
9 #include "supernovaecomponent.h"
10 
11 #ifndef KSTARS_LITE
12 #include "kstars.h"
13 #include "skymap.h"
14 #else
15 #include "kstarslite.h"
16 #endif
17 #include "dms.h"
18 #include "kstars_debug.h"
19 #include "ksnotification.h"
20 #include "kstarsdata.h"
21 #include "Options.h"
22 #include "skylabeler.h"
23 #include "skymesh.h"
24 #include "skypainter.h"
25 #include "auxiliary/filedownloader.h"
26 #include "projections/projector.h"
27 #include "auxiliary/kspaths.h"
28 
29 #include <QtConcurrent>
30 #include <QJsonDocument>
31 #include <QJsonValue>
32 
SupernovaeComponent(SkyComposite * parent)33 SupernovaeComponent::SupernovaeComponent(SkyComposite *parent) : ListComponent(parent)
34 {
35     //QtConcurrent::run(this, &SupernovaeComponent::loadData);
36     //loadData();
37 }
38 
update(KSNumbers * num)39 void SupernovaeComponent::update(KSNumbers *num)
40 {
41     if (!selected() || !m_DataLoaded)
42         return;
43 
44     KStarsData *data = KStarsData::Instance();
45     for (auto so : m_ObjectList)
46     {
47         if (num)
48             so->updateCoords(num);
49         so->EquatorialToHorizontal(data->lst(), data->geo()->lat());
50     }
51 }
52 
selected()53 bool SupernovaeComponent::selected()
54 {
55     return Options::showSupernovae();
56 }
57 
loadData()58 void SupernovaeComponent::loadData()
59 {
60     qDeleteAll(m_ObjectList);
61     m_ObjectList.clear();
62 
63     objectNames(SkyObject::SUPERNOVA).clear();
64     objectLists(SkyObject::SUPERNOVA).clear();
65 
66     QString name, type, host, date, ra, de;
67     float z, mag;
68 
69     QString sFileName = KSPaths::locate(QStandardPaths::AppDataLocation, QString("catalog.min.json"));
70 
71     QFile sNovaFile(sFileName);
72 
73     if (sNovaFile.open(QIODevice::ReadOnly) == false)
74     {
75         qCritical() << "Unable to open supernova file" << sFileName;
76         return;
77     }
78 
79     QJsonParseError pError;
80     QJsonDocument sNova = QJsonDocument::fromJson(sNovaFile.readAll(), &pError);
81 
82     if (pError.error != QJsonParseError::NoError)
83     {
84         qCritical() << "Error parsing json document" << pError.errorString();
85         return;
86     }
87 
88     if (sNova.isArray() == false)
89     {
90         qCCritical(KSTARS) << "Invalid document format! No JSON array.";
91         return;
92     }
93 
94     QJsonArray sArray = sNova.array();
95     bool ok           = false;
96 
97     for (const auto snValue : sArray)
98     {
99         const QJsonObject propObject = snValue.toObject();
100         mag                          = 99.9;
101         z                            = 0;
102         name.clear();
103         type.clear();
104         host.clear();
105         date.clear();
106 
107         if (propObject.contains("ra") == false || propObject.contains("dec") == false)
108             continue;
109         ra = ((propObject["ra"].toArray()[0]).toObject()["value"]).toString();
110         de = ((propObject["dec"].toArray()[0]).toObject()["value"]).toString();
111 
112         name = propObject["name"].toString();
113         if (propObject.contains("claimedtype"))
114             type = ((propObject["claimedtype"].toArray()[0]).toObject()["value"]).toString();
115         if (propObject.contains("host"))
116             host = ((propObject["host"].toArray()[0]).toObject()["value"]).toString();
117         if (propObject.contains("discoverdate"))
118             date = ((propObject["discoverdate"].toArray()[0]).toObject()["value"]).toString();
119         if (propObject.contains("redshift"))
120             z = ((propObject["redshift"].toArray()[0]).toObject()["value"]).toString().toDouble(&ok);
121         if (ok == false)
122             z = 99.9;
123         if (propObject.contains("maxappmag"))
124             mag = ((propObject["maxappmag"].toArray()[0]).toObject()["value"]).toString().toDouble(&ok);
125         if (ok == false)
126             mag = 99.9;
127 
128         Supernova *sup =
129             new Supernova(name, dms::fromString(ra, false), dms::fromString(de, true), type, host, date, z, mag);
130 
131         objectNames(SkyObject::SUPERNOVA).append(name);
132 
133         appendListObject(sup);
134         objectLists(SkyObject::SUPERNOVA).append(QPair<QString, const SkyObject *>(name, sup));
135     }
136 
137     m_DataLoading = false;
138     m_DataLoaded = true;
139 }
140 
objectNearest(SkyPoint * p,double & maxrad)141 SkyObject *SupernovaeComponent::objectNearest(SkyPoint *p, double &maxrad)
142 {
143     if (!selected() || !m_DataLoaded)
144         return nullptr;
145 
146     SkyObject *oBest = nullptr;
147     double rBest     = maxrad;
148 
149     for (auto so : m_ObjectList)
150     {
151         double r = so->angularDistanceTo(p).Degrees();
152         //qDebug()<<r;
153         if (r < rBest)
154         {
155             oBest = so;
156             rBest = r;
157         }
158     }
159     maxrad = rBest;
160     return oBest;
161 }
162 
zoomMagnitudeLimit()163 float SupernovaeComponent::zoomMagnitudeLimit()
164 {
165     //adjust maglimit for ZoomLevel
166     double lgmin = log10(MINZOOM);
167     double lgz   = log10(Options::zoomFactor());
168 
169     return 14.0 + 2.222 * (lgz - lgmin) + 2.222 * log10(static_cast<double>(Options::starDensity()));
170 }
171 
draw(SkyPainter * skyp)172 void SupernovaeComponent::draw(SkyPainter *skyp)
173 {
174     //qDebug()<<"selected()="<<selected();
175     if (!selected())
176         return;
177     else if (!m_DataLoaded)
178     {
179         if (!m_DataLoading)
180         {
181             m_DataLoading = true;
182             QtConcurrent::run(this, &SupernovaeComponent::loadData);
183         }
184         return;
185     }
186 
187     double maglim = zoomMagnitudeLimit();
188 
189     for (auto so : m_ObjectList)
190     {
191         Supernova *sup = dynamic_cast<Supernova *>(so);
192         float mag      = sup->mag();
193 
194         if (mag > float(Options::magnitudeLimitShowSupernovae()))
195             continue;
196 
197         //Do not draw if mag>maglim
198         if (mag > maglim)
199             continue;
200         skyp->drawSupernova(sup);
201     }
202 }
203 
204 #if 0
205 void SupernovaeComponent::notifyNewSupernovae()
206 {
207 #ifndef KSTARS_LITE
208     //qDebug()<<"New Supernovae discovered";
209     QList<SkyObject *> latestList;
210     foreach (SkyObject * so, latest)
211     {
212         Supernova * sup = (Supernova *)so;
213 
214         if (sup->getMagnitude() > float(Options::magnitudeLimitAlertSupernovae()))
215         {
216             //qDebug()<<"Not Bright enough to be notified";
217             continue;
218         }
219 
220         //qDebug()<<"Bright enough to be notified";
221         latestList.append(so);
222     }
223     if (!latestList.empty())
224     {
225         NotifyUpdatesUI * ui = new NotifyUpdatesUI(0);
226         ui->addItems(latestList);
227         ui->show();
228     }
229     //     if (!latest.empty())
230     //         KMessageBox::informationList(0, i18n("New Supernovae discovered!"), latestList, i18n("New Supernovae discovered!"));
231 #endif
232 }
233 #endif
234 
slotTriggerDataFileUpdate()235 void SupernovaeComponent::slotTriggerDataFileUpdate()
236 {
237     delete(downloadJob);
238     downloadJob = new FileDownloader();
239 
240     downloadJob->setProgressDialogEnabled(true, i18n("Supernovae Update"), i18n("Downloading Supernovae updates..."));
241 
242     QObject::connect(downloadJob, SIGNAL(downloaded()), this, SLOT(downloadReady()));
243     QObject::connect(downloadJob, SIGNAL(error(QString)), this, SLOT(downloadError(QString)));
244 
245     QString output = QDir(KSPaths::writableLocation(QStandardPaths::AppDataLocation)).filePath("catalog.min.json");
246 
247     downloadJob->setDownloadedFileURL(QUrl::fromLocalFile(output));
248 
249     downloadJob->get(QUrl("https://sne.space/astrocats/astrocats/supernovae/output/catalog.min.json"));
250 }
251 
downloadReady()252 void SupernovaeComponent::downloadReady()
253 {
254     // Reload Supernova
255     loadData();
256 #ifdef KSTARS_LITE
257     KStarsLite::Instance()->data()->setFullTimeUpdate();
258 #else
259     KStars::Instance()->data()->setFullTimeUpdate();
260 #endif
261     downloadJob->deleteLater();
262 }
263 
downloadError(const QString & errorString)264 void SupernovaeComponent::downloadError(const QString &errorString)
265 {
266     KSNotification::error(i18n("Error downloading supernova data: %1", errorString));
267     downloadJob->deleteLater();
268 }
269