1 /*
2     SPDX-FileCopyrightText: 2009 Jerome SONRIER <jsid@emor3j.fr.eu.org>
3 
4     SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "satellitescomponent.h"
8 
9 #include "ksfilereader.h"
10 #include "ksnotification.h"
11 #include "kstarsdata.h"
12 #include "Options.h"
13 #include "skylabeler.h"
14 #include "skymap.h"
15 #include "skypainter.h"
16 #include "skyobjects/satellite.h"
17 
18 #include <QNetworkAccessManager>
19 #include <QNetworkReply>
20 #include <QProgressDialog>
21 #include <QtConcurrent>
22 
SatellitesComponent(SkyComposite * parent)23 SatellitesComponent::SatellitesComponent(SkyComposite *parent) : SkyComponent(parent)
24 {
25     QtConcurrent::run(this, &SatellitesComponent::loadData);
26 }
27 
~SatellitesComponent()28 SatellitesComponent::~SatellitesComponent()
29 {
30     qDeleteAll(m_groups);
31     m_groups.clear();
32 }
33 
loadData()34 void SatellitesComponent::loadData()
35 {
36     KSFileReader fileReader;
37     QString line;
38     QStringList group_infos;
39 
40     if (!fileReader.open("satellites.dat"))
41         return;
42 
43     emitProgressText(i18n("Loading satellites"));
44 
45     while (fileReader.hasMoreLines())
46     {
47         line = fileReader.readLine();
48         if (line.trimmed().isEmpty() || line.at(0) == '#')
49             continue;
50         group_infos = line.split(';');
51         m_groups.append(new SatelliteGroup(group_infos.at(0), group_infos.at(1), QUrl(group_infos.at(2))));
52     }
53 
54     objectNames(SkyObject::SATELLITE).clear();
55     objectLists(SkyObject::SATELLITE).clear();
56 
57     foreach (SatelliteGroup *group, m_groups)
58     {
59         for (int i = 0; i < group->size(); i++)
60         {
61             Satellite *sat = group->at(i);
62 
63             if (sat->selected() && nameHash.contains(sat->name().toLower()) == false)
64             {
65                 objectNames(SkyObject::SATELLITE).append(sat->name());
66                 objectLists(SkyObject::SATELLITE).append(QPair<QString, const SkyObject *>(sat->name(), sat));
67                 nameHash[sat->name().toLower()] = sat;
68             }
69         }
70     }
71 }
72 
selected()73 bool SatellitesComponent::selected()
74 {
75     return Options::showSatellites();
76 }
77 
update(KSNumbers *)78 void SatellitesComponent::update(KSNumbers *)
79 {
80     // Return if satellites must not be draw
81     if (!selected())
82         return;
83 
84     foreach (SatelliteGroup *group, m_groups)
85     {
86         group->updateSatellitesPos();
87     }
88 }
89 
draw(SkyPainter * skyp)90 void SatellitesComponent::draw(SkyPainter *skyp)
91 {
92 #ifndef KSTARS_LITE
93     // Return if satellites must not be draw
94     if (!selected())
95         return;
96 
97     bool hideLabels = (!Options::showSatellitesLabels() || (SkyMap::Instance()->isSlewing() && Options::hideLabels()));
98 
99     foreach (SatelliteGroup *group, m_groups)
100     {
101         for (int i = 0; i < group->size(); i++)
102         {
103             Satellite *sat = group->at(i);
104 
105             if (sat->selected())
106             {
107                 bool drawn = false;
108                 if (Options::showVisibleSatellites())
109                 {
110                     if (sat->isVisible())
111                         drawn = skyp->drawSatellite(sat);
112                 }
113                 else
114                 {
115                     drawn = skyp->drawSatellite(sat);
116                 }
117 
118                 if (drawn && !hideLabels)
119                     SkyLabeler::AddLabel(sat, SkyLabeler::SATELLITE_LABEL);
120             }
121         }
122     }
123 #else
124     Q_UNUSED(skyp);
125 #endif
126 }
127 
drawLabel(Satellite * sat,const QPointF & pos)128 void SatellitesComponent::drawLabel(Satellite *sat, const QPointF& pos)
129 {
130     SkyLabeler *labeler = SkyLabeler::Instance();
131     labeler->setPen(KStarsData::Instance()->colorScheme()->colorNamed("SatLabelColor"));
132     labeler->drawNameLabel(sat, pos);
133 }
134 
drawTrails(SkyPainter * skyp)135 void SatellitesComponent::drawTrails(SkyPainter *skyp)
136 {
137     Q_UNUSED(skyp);
138 }
139 
updateTLEs()140 void SatellitesComponent::updateTLEs()
141 {
142     int i = 0;
143     QProgressDialog progressDlg(i18n("Update TLEs..."), i18n("Abort"), 0, m_groups.count());
144     progressDlg.setWindowModality(Qt::WindowModal);
145     progressDlg.setValue(0);
146 
147     foreach (SatelliteGroup *group, m_groups)
148     {
149         if (progressDlg.wasCanceled())
150             return;
151 
152         if (group->tleUrl().isEmpty())
153             continue;
154 
155         progressDlg.setLabelText(i18n("Update %1 satellites", group->name()));
156         progressDlg.setWindowTitle(i18nc("@title:window", "Satellite Orbital Elements Update"));
157 
158         QNetworkAccessManager manager;
159         QNetworkReply *response = manager.get(QNetworkRequest(group->tleUrl()));
160 
161         // Wait synchronously
162         QEventLoop event;
163         QObject::connect(response, SIGNAL(finished()), &event, SLOT(quit()));
164         event.exec();
165 
166         if (response->error() == QNetworkReply::NoError)
167         {
168             QFile file(group->tleFilename().toLocalFile());
169             if (file.open(QFile::WriteOnly))
170             {
171                 file.write(response->readAll());
172                 file.close();
173                 group->readTLE();
174                 group->updateSatellitesPos();
175                 progressDlg.setValue(++i);
176             }
177             else
178             {
179                 KSNotification::error(file.errorString());
180                 return;
181             }
182         }
183         else
184         {
185             KSNotification::error(response->errorString());
186             return;
187         }
188     }
189 }
190 
groups()191 QList<SatelliteGroup *> SatellitesComponent::groups()
192 {
193     return m_groups;
194 }
195 
findSatellite(QString name)196 Satellite *SatellitesComponent::findSatellite(QString name)
197 {
198     foreach (SatelliteGroup *group, m_groups)
199     {
200         for (int i = 0; i < group->size(); i++)
201         {
202             Satellite *sat = group->at(i);
203             if (sat->name() == name)
204                 return sat;
205         }
206     }
207 
208     return nullptr;
209 }
210 
objectNearest(SkyPoint * p,double & maxrad)211 SkyObject *SatellitesComponent::objectNearest(SkyPoint *p, double &maxrad)
212 {
213     if (!selected())
214         return nullptr;
215 
216     //KStarsData* data = KStarsData::Instance();
217 
218     SkyObject *oBest = nullptr;
219     double rBest     = maxrad;
220     double r;
221 
222     foreach (SatelliteGroup *group, m_groups)
223     {
224         for (int i = 0; i < group->size(); i++)
225         {
226             Satellite *sat = group->at(i);
227             if (!sat->selected())
228                 continue;
229 
230             r = sat->angularDistanceTo(p).Degrees();
231             //qDebug() << sat->name();
232             //qDebug() << "r = " << r << " - max = " << rBest;
233             //qDebug() << "ra2=" << sat->ra().Degrees() << " - dec2=" << sat->dec().Degrees();
234             if (r < rBest)
235             {
236                 rBest = r;
237                 oBest = sat;
238             }
239         }
240     }
241 
242     maxrad = rBest;
243     return oBest;
244 }
245 
findByName(const QString & name)246 SkyObject *SatellitesComponent::findByName(const QString &name)
247 {
248     return nameHash[name.toLower()];
249 }
250