Kstars

asteroidscomponent.cpp
1/*
2 SPDX-FileCopyrightText: 2005 Thomas Kabelmann <thomas.kabelmann@gmx.de>
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
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 */
81void 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
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
171
172 // Add name to the list of object names
173 objectNames(SkyObject::ASTEROID).append(name);
174 objectLists(SkyObject::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{
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]
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.
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
254void 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();
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
283void 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
300
301#ifdef KSTARS_LITE
302 SkyObject *foc = KStarsLite::Instance()->map()->focusObject();
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
321 KStarsLite::Instance()->data()->setFullTimeUpdate();
322 if (!focusedAstroid.isEmpty())
323 KStarsLite::Instance()->map()->setFocusObject(
324 KStarsLite::Instance()->data()->objectNamed(focusedAstroid));
325#else
326 if (!focusedAstroid.isEmpty())
327 KStars::Instance()->map()->setFocusObject(
328 KStars::Instance()->data()->objectNamed(focusedAstroid));
329 KStars::Instance()->data()->setFullTimeUpdate();
330#endif
331 downloadJob->deleteLater();
332}
333
334void 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}
AsteroidsComponent(SolarSystemComposite *parent)
Default constructor.
void draw(SkyPainter *skyp) override
Draw the object on the SkyMap skyp a pointer to the SkyPainter to use.
SkyObject * objectNearest(SkyPoint *p, double &maxrad) override
Find the SkyObject nearest the given SkyPoint.
provides functionality for loading the component data from Binary
A subclass of KSPlanetBase that implements asteroids.
Definition ksasteroid.h:42
static KStarsLite * Instance()
Definition kstarslite.h:77
static KStars * Instance()
Definition kstars.h:123
void appendListObject(SkyObject *object)
Add an object to the Object list.
virtual void emitProgressText(const QString &message)
Emit signal about progress.
static void AddLabel(SkyObject *obj, label_t type)
static version of addLabel() below.
Definition skylabeler.h:135
Provides all necessary information about an object in the sky: its coordinates, name(s),...
Definition skyobject.h:42
Draws things on the sky, without regard to backend.
Definition skypainter.h:40
The sky coordinates of a point in the sky.
Definition skypoint.h:45
The solar system composite manages all planets, asteroids and comets.
An angle, stored as degrees, but expressible in many ways.
Definition dms.h:38
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
KIOCORE_EXPORT TransferJob * get(const QUrl &url, LoadType reload=NoReload, JobFlags flags=DefaultFlags)
void error(QWidget *parent, const QString &text, const QString &title, const KGuiItem &buttonOk, Options options=Notify)
bool startsWith(QByteArrayView bv) const const
Int toInt() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
QString number(double n, char format, int precision)
QByteArray toUtf8() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:19:04 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.