Kstars

asteroidscomponent.cpp
1 /*
2  SPDX-FileCopyrightText: 2005 Thomas Kabelmann <[email protected]>
3 
4  SPDX-License-Identifier: GPL-2.0-or-later
5 */
6 
7 #include "asteroidscomponent.h"
8 #include "ksutils.h"
9 
10 #ifndef KSTARS_LITE
11 #include "kstars.h"
12 #endif
13 #include "ksfilereader.h"
14 #include "kstarsdata.h"
15 #include "kstars_debug.h"
16 #include "Options.h"
17 #include "solarsystemcomposite.h"
18 #include "skycomponent.h"
19 #include "skylabeler.h"
20 #ifndef KSTARS_LITE
21 #include "skymap.h"
22 #else
23 #include "kstarslite.h"
24 #endif
25 #include "skypainter.h"
26 #include "auxiliary/kspaths.h"
27 #include "auxiliary/ksnotification.h"
28 #include "auxiliary/filedownloader.h"
29 #include "projections/projector.h"
30 
31 #include <KLocalizedString>
32 
33 #include <QDebug>
34 #include <QStandardPaths>
35 #include <QHttpMultiPart>
36 #include <QPen>
37 
38 #include <cmath>
39 
41  : BinaryListComponent(this, "asteroids"), SolarSystemListComponent(parent)
42 {
43  loadData();
44 }
45 
47 {
48  return Options::showAsteroids();
49 }
50 
51 /*
52  * @short Initialize the asteroids list.
53  * Reads in the asteroids data from the asteroids.dat file
54  * and writes it into the Binary File;
55  *
56  * The data file is a CSV file with the following columns :
57  * @li 1 full name [string]
58  * @li 2 Modified Julian Day of orbital elements [int]
59  * @li 3 perihelion distance in AU [double]
60  * @li 4 semi-major axis
61  * @li 5 eccentricity of orbit [double]
62  * @li 6 inclination angle of orbit in degrees [double]
63  * @li 7 argument of perihelion in degrees [double]
64  * @li 8 longitude of the ascending node in degrees [double]
65  * @li 9 mean anomaly
66  * @li 10 time of perihelion passage (YYYYMMDD.DDD) [double]
67  * @li 11 orbit solution ID [string]
68  * @li 12 absolute magnitude [float]
69  * @li 13 slope parameter [float]
70  * @li 14 Near-Earth Object (NEO) flag [bool]
71  * @li 15 comet total magnitude parameter [float] (we should remove this column)
72  * @li 16 comet nuclear magnitude parameter [float] (we should remove this column)
73  * @li 17 object diameter (from equivalent sphere) [float]
74  * @li 18 object bi/tri-axial ellipsoid dimensions [string]
75  * @li 19 geometric albedo [float]
76  * @li 20 rotation period [float]
77  * @li 21 orbital period [float]
78  * @li 22 earth minimum orbit intersection distance [double]
79  * @li 23 orbit classification [string]
80  */
81 void AsteroidsComponent::loadDataFromText()
82 {
83  QString name, full_name, orbit_id, orbit_class, dimensions;
84  int mJD;
85  double q, a, e, dble_i, dble_w, dble_N, dble_M, H, G, earth_moid;
86  long double JD;
87  float diameter, albedo, rot_period, period;
88  bool neo;
89 
90  emitProgressText(i18n("Loading asteroids"));
91  qCInfo(KSTARS) << "Loading asteroids";
92 
93  try
94  {
95  KSUtils::JPLParser ast_parser(filepath_txt);
96  auto fieldMap = ast_parser.fieldMap();
97  bool isString = fieldMap.count("epoch_mjd") == 1;
98 
99  ast_parser.for_each(
100  [&](const auto & get)
101  {
102  full_name = get("full_name").toString();
103  full_name = full_name.trimmed();
104  int catN = full_name.section(' ', 0, 0).toInt();
105  name = full_name.section(' ', 1, -1);
106 
107  //JM temporary hack to avoid Europa,Io, and Asterope duplication
108  if (name == i18nc("Asteroid name (optional)", "Europa") ||
109  name == i18nc("Asteroid name (optional)", "Io") ||
110  name == i18nc("Asteroid name (optional)", "Asterope"))
111  name += i18n(" (Asteroid)");
112 
113  // JM 2022.08.26: Try to check if the file is in the new format
114  // where epoch_mjd field is a string
115  if (isString)
116  {
117  mJD = get("epoch_mjd").toString().toInt();
118  period = get("per_y").toString().toDouble();
119  }
120  // If not fall back to old behavior
121  else
122  {
123  mJD = get("epoch.mjd").toInt();
124  period = get("per.y").toDouble();
125  }
126 
127  q = get("q").toString().toDouble();
128  a = get("a").toString().toDouble();
129  e = get("e").toString().toDouble();
130  dble_i = get("i").toString().toDouble();
131  dble_w = get("w").toString().toDouble();
132  dble_N = get("om").toString().toDouble();
133  dble_M = get("ma").toString().toDouble();
134  orbit_id = get("orbit_id").toString();
135  H = get("H").toString().toDouble();
136  G = get("G").toString().toDouble();
137  neo = get("neo").toString() == "Y";
138  diameter = get("diameter").toString().toFloat();
139  dimensions = get("extent").toString();
140  albedo = get("albedo").toString().toFloat();
141  rot_period = get("rot_per").toString().toFloat();
142  earth_moid = get("moid").toString().toDouble();
143  orbit_class = get("class").toString();
144 
145  JD = static_cast<double>(mJD) + 2400000.5;
146 
147  KSAsteroid *new_asteroid = nullptr;
148 
149  // Diameter is missing from JPL data
150  if (name == i18nc("Asteroid name (optional)", "Pluto"))
151  diameter = 2390;
152 
153  new_asteroid =
154  new KSAsteroid(catN, name, QString(), JD, a, e, dms(dble_i),
155  dms(dble_w), dms(dble_N), dms(dble_M), H, G);
156 
157  new_asteroid->setPerihelion(q);
158  new_asteroid->setOrbitID(orbit_id);
159  new_asteroid->setNEO(neo);
160  new_asteroid->setDiameter(diameter);
161  new_asteroid->setDimensions(dimensions);
162  new_asteroid->setAlbedo(albedo);
163  new_asteroid->setRotationPeriod(rot_period);
164  new_asteroid->setPeriod(period);
165  new_asteroid->setEarthMOID(earth_moid);
166  new_asteroid->setOrbitClass(orbit_class);
167  new_asteroid->setPhysicalSize(diameter);
168  //new_asteroid->setAngularSize(0.005);
169 
170  appendListObject(new_asteroid);
171 
172  // Add name to the list of object names
173  objectNames(SkyObject::ASTEROID).append(name);
174  objectLists(SkyObject::ASTEROID)
175  .append(QPair<QString, const SkyObject *>(name, new_asteroid));
176  });
177  }
178  catch (const std::runtime_error &e)
179  {
180  qCInfo(KSTARS) << "Loading asteroid objects failed.";
181  qCInfo(KSTARS) << " -> was trying to read " + filepath_txt;
182  return;
183  }
184 }
185 
187 {
188  Q_UNUSED(skyp)
189 #ifndef KSTARS_LITE
190  if (!selected())
191  return;
192 
193  bool hideLabels = !Options::showAsteroidNames() || (SkyMap::Instance()->isSlewing() && Options::hideLabels());
194 
195  double labelMagLimit = Options::asteroidLabelDensity(); // Slider min value 0, max value 20.
196  const double showMagLimit = Options::magLimitAsteroid();
197  const double lgmin = log10(MINZOOM);
198  const double lgmax = log10(MAXZOOM);
199  const double lgz = log10(Options::zoomFactor());
200  const double densityLabelFactor = 10.0; // Value of 10.0 influences the slider mag value [0, 2],
201  // where a value 5.0 influences the slider mag value [0, 4].
202  const double zoomLimit = (lgz - lgmin) / (lgmax - lgmin); // Min-max normalize into [lgmin, lgmax].
203 
204  // Map labelMagLimit into interval [0, 20.0 / densityLabelFactor]
205  labelMagLimit = std::max(1e-3, labelMagLimit) / densityLabelFactor;
206  // If zooming closer, then the labelMagLimit gets closer to showMagLimit value.
207  // If sliding density value to the right, then the labelMagLimit gets closer to showMagLimit value.
208  // It is however assured that labelMagLimit <= showMagLimit.
209  labelMagLimit = showMagLimit - 20.0 / densityLabelFactor + std::max(zoomLimit, labelMagLimit);
210 
211  foreach (SkyObject *so, m_ObjectList)
212  {
213  KSAsteroid *ast = dynamic_cast<KSAsteroid *>(so);
214 
215  if (!ast->toDraw() || std::isnan(ast->mag()) || ast->mag() > showMagLimit)
216  continue;
217 
218  bool drawn = false;
219 
220  if (ast->image().isNull() == false)
221  drawn = skyp->drawPlanet(ast);
222  else
223  drawn = skyp->drawAsteroid(ast);
224 
225  if (drawn && !hideLabels && ast->mag() <= labelMagLimit)
226  SkyLabeler::AddLabel(ast, SkyLabeler::ASTEROID_LABEL);
227  }
228 #endif
229 }
230 
232 {
233  SkyObject *oBest = nullptr;
234 
235  if (!selected())
236  return nullptr;
237 
238  for (auto o : m_ObjectList)
239  {
240  if (!((dynamic_cast<KSAsteroid*>(o)->toDraw())))
241  continue;
242 
243  double r = o->angularDistanceTo(p).Degrees();
244  if (r < maxrad)
245  {
246  oBest = o;
247  maxrad = r;
248  }
249  }
250 
251  return oBest;
252 }
253 
254 void AsteroidsComponent::updateDataFile(bool isAutoUpdate)
255 {
256  delete (downloadJob);
257  downloadJob = new FileDownloader();
258 
259  if (isAutoUpdate == false)
260  downloadJob->setProgressDialogEnabled(true, i18n("Asteroid Update"),
261  i18n("Downloading asteroids updates..."));
262  downloadJob->registerDataVerification([&](const QByteArray & data)
263  {
264  return data.startsWith("{\"signature\"");
265  });
266 
267  QObject::connect(downloadJob, SIGNAL(downloaded()), this, SLOT(downloadReady()));
268  if (isAutoUpdate == false)
269  QObject::connect(downloadJob, SIGNAL(error(QString)), this,
270  SLOT(downloadError(QString)));
271 
272  QUrl url = QUrl("https://ssd-api.jpl.nasa.gov/sbdb_query.api");
273 
274  QByteArray mag = QString::number(Options::magLimitAsteroidDownload()).toUtf8();
275  QByteArray post_data =
276  KSUtils::getJPLQueryString("a",
277  "full_name,neo,H,G,diameter,extent,albedo,rot_per,"
278  "orbit_id,epoch_mjd,e,a,q,i,om,w,ma,per_y,moid,class",
279  QVector<KSUtils::JPLFilter> { { "H", "LT", mag } });
280  downloadJob->post(url, post_data);
281 }
282 
283 void AsteroidsComponent::downloadReady()
284 {
285  // Comment the first line
286  QByteArray data = downloadJob->downloadedData();
287 
288  // Write data to asteroids.dat
289  QFile file(QDir(KSPaths::writableLocation(QStandardPaths::AppLocalDataLocation))
290  .filePath("asteroids.dat"));
292  {
293  file.write(data);
294  file.close();
295  }
296  else
297  qCWarning(KSTARS) << "Failed writing asteroid data to" << file.fileName();
298 
299  QString focusedAstroid;
300 
301 #ifdef KSTARS_LITE
303  if (foc && foc->type() == SkyObject::ASTEROID)
304  {
305  focusedAstroid = foc->name();
306  KStarsLite::Instance()->map()->setFocusObject(nullptr);
307  }
308 #else
309  SkyObject *foc = KStars::Instance()->map()->focusObject();
310  if (foc && foc->type() == SkyObject::ASTEROID)
311  {
312  focusedAstroid = foc->name();
313  KStars::Instance()->map()->setFocusObject(nullptr);
314  }
315 
316 #endif
317  // Reload asteroids
318  loadData(true);
319 
320 #ifdef KSTARS_LITE
322  if (!focusedAstroid.isEmpty())
324  KStarsLite::Instance()->data()->objectNamed(focusedAstroid));
325 #else
326  if (!focusedAstroid.isEmpty())
328  KStars::Instance()->data()->objectNamed(focusedAstroid));
330 #endif
331  downloadJob->deleteLater();
332 }
333 
334 void AsteroidsComponent::downloadError(const QString &errorString)
335 {
336  KSNotification::error(i18n("Error downloading asteroids data: %1", errorString));
337  qDebug() << Q_FUNC_INFO << i18n("Error downloading asteroids data: %1", errorString);
338  downloadJob->deleteLater();
339 }
QString section(QChar sep, int start, int end, QString::SectionFlags flags) const const
void setOrbitID(QString orbit_id)
Sets the asteroid's orbit solution ID.
Definition: ksasteroid.cpp:195
QString number(int n, int base)
void setDimensions(QString dim)
Sets the asteroid's dimensions.
Definition: ksasteroid.cpp:180
void setOrbitClass(QString orbit_class)
Sets the asteroid's orbit class.
Definition: ksasteroid.cpp:190
static void AddLabel(SkyObject *obj, label_t type)
static version of addLabel() below.
Definition: skylabeler.h:135
provides functionality for loading the component data from Binary
Stores dms coordinates for a point in the sky. for converting between coordinate systems.
Definition: skypoint.h:44
KStarsData * data() const
Definition: kstarslite.h:86
QString trimmed() const const
void setAlbedo(float albedo)
Sets the asteroid's albedo.
Definition: ksasteroid.cpp:170
A subclass of KSPlanetBase that implements asteroids.
Definition: ksasteroid.h:41
virtual QString name(void) const
Definition: skyobject.h:145
static KStarsLite * Instance()
Definition: kstarslite.h:77
void draw(SkyPainter *skyp) override
Draw the object on the SkyMap skyp a pointer to the SkyPainter to use.
float mag() const
Definition: skyobject.h:206
SkyMap * map() const
Definition: kstars.h:141
void setFullTimeUpdate()
The Sky is updated more frequently than the moon, which is updated more frequently than the planets.
Definition: kstarsdata.cpp:313
void setEarthMOID(double earth_moid)
Sets the asteroid's earth minimum orbit intersection distance.
Definition: ksasteroid.cpp:165
void setFocusObject(SkyObject *o)
Set the FocusObject pointer to the argument.
Definition: skymap.cpp:368
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
void setDiameter(float diam)
Sets the asteroid's diameter.
Definition: ksasteroid.cpp:175
SkyMapLite * map() const
Definition: kstarslite.h:80
static KStars * Instance()
Definition: kstars.h:123
int type(void) const
Definition: skyobject.h:188
virtual void emitProgressText(const QString &message)
Emit signal about progress.
SkyObject * objectNearest(SkyPoint *p, double &maxrad) override
Find the SkyObject nearest the given SkyPoint.
double toDouble(bool *ok) const const
QString i18n(const char *text, const TYPE &arg...)
const QImage & image() const
Definition: ksplanetbase.h:127
bool isEmpty() const const
void appendListObject(SkyObject *object)
Add an object to the Object list.
QByteArray toUtf8() const const
bool isNull() const const
int toInt(bool *ok) const const
bool startsWith(const QByteArray &ba) const const
int toInt(bool *ok, int base) const const
bool selected() override
float toFloat(bool *ok) const const
SkyObject * focusObject() const
Retrieve the object which is centered in the sky map.
Definition: skymaplite.h:252
void setNEO(bool neo)
Sets if the comet is a near earth object.
Definition: ksasteroid.cpp:185
Draws things on the sky, without regard to backend.
Definition: skypainter.h:39
void setRotationPeriod(float rot_per)
Sets the asteroid's rotation period.
Definition: ksasteroid.cpp:263
dms angularDistanceTo(const SkyPoint *sp, double *const positionAngle=nullptr) const
Computes the angular distance between two SkyObjects.
Definition: skypoint.cpp:899
double toDouble(bool *ok) const const
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
void setFocusObject(SkyObject *o)
Set the FocusObject pointer to the argument.
Definition: skymaplite.cpp:318
An angle, stored as degrees, but expressible in many ways.
Definition: dms.h:37
bool toDraw()
toDraw
Definition: ksasteroid.h:188
SkyObject * focusObject() const
Retrieve the object which is centered in the sky map.
Definition: skymap.h:262
const double & Degrees() const
Definition: dms.h:141
virtual bool drawAsteroid(KSAsteroid *ast)=0
Draw an asteroid in the sky.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
void setPerihelion(double perihelion)
Sets the asteroid's perihelion distance.
Definition: ksasteroid.cpp:160
KStarsData * data() const
Definition: kstars.h:135
void setPhysicalSize(double size)
set the planet's physical size, in km.
Definition: ksplanetbase.h:197
Information about an object in the sky.
Definition: skyobject.h:41
AsteroidsComponent(SolarSystemComposite *parent)
Default constructor.
virtual bool drawPlanet(KSPlanetBase *planet)=0
Draw a planet.
virtual QVariant get(ScriptableExtension *callerPrincipal, quint64 objId, const QString &propName)
QString toString() const const
void setPeriod(float per)
Sets the asteroid's period.
Definition: ksasteroid.cpp:200
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Wed Nov 29 2023 04:05:38 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.