Kstars

ksdssdownloader.cpp
1/*
2 SPDX-FileCopyrightText: 2016 Akarsh Simha <akarsh.simha@kdemail.net>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "ksdssdownloader.h"
8
9#include "Options.h"
10#include "auxiliary/filedownloader.h"
11#include "catalogobject.h"
12
13#include <QImageWriter>
14#include <QMimeDatabase>
15
16
18{
19 connect(this, &KSDssDownloader::downloadCanceled, this, [&]() { deleteLater(); });
20 m_VersionPreference << "poss2ukstu_blue"
21 << "poss2ukstu_red"
22 << "poss1_blue"
23 << "poss1_red"
24 << "quickv"
25 << "poss2ukstu_ir";
26 m_TempFile.open();
27}
28
30 const std::function<void(bool)> &slotDownloadReady, QObject *parent)
31 : QObject(parent)
32{
33 // Initialize version preferences. FIXME: This must be made a
34 // user-changeable option just in case someone likes red
35
36 connect(this, &KSDssDownloader::downloadCanceled, this, [&]() { deleteLater(); });
37
38 m_VersionPreference << "poss2ukstu_blue"
39 << "poss2ukstu_red"
40 << "poss1_blue"
41 << "poss1_red"
42 << "quickv"
43 << "poss2ukstu_ir";
44 m_TempFile.open();
45 connect(this, &KSDssDownloader::downloadComplete, slotDownloadReady);
46 startDownload(p, destFileName);
47}
48
50{
51 double height, width;
52
53 double dss_default_size = Options::defaultDSSImageSize();
54 double dss_padding = Options::dSSPadding();
55
56 Q_ASSERT(p);
57 Q_ASSERT(dss_default_size > 0.0 && dss_padding >= 0.0);
58
59 const auto * dso = dynamic_cast<const CatalogObject *>(p);
60
61 // Decide what to do about the height and width
62 if (dso)
63 {
64 // For deep-sky objects, use their height and width information
65 double a, b, pa;
66 a = dso->a();
67 b = dso->a() *
68 dso->e(); // Use a * e instead of b, since e() returns 1 whenever one of the dimensions is zero. This is important for circular objects
69 Q_ASSERT(a >= b);
70 pa = dso->pa() * dms::DegToRad;
71
72 // We now want to convert a, b, and pa into an image
73 // height and width -- i.e. a dRA and dDec.
74 // DSS uses dDec for height and dRA for width. (i.e. "top" is north in the DSS images, AFAICT)
75 // From some trigonometry, assuming we have a rectangular object (worst case), we need:
76 width = a * sin(pa) + b * fabs(cos(pa));
77 height = a * fabs(cos(pa)) + b * sin(pa);
78 // 'a' and 'b' are in arcminutes, so height and width are in arcminutes
79
80 // Pad the RA and Dec, so that we show more of the sky than just the object.
81 height += dss_padding;
82 width += dss_padding;
83 }
84 else
85 {
86 // For a generic sky object, we don't know what to do. So
87 // we just assume the default size.
88 height = width = dss_default_size;
89 }
90 // There's no point in tiny DSS images that are smaller than dss_default_size
91 if (height < dss_default_size)
92 height = dss_default_size;
93 if (width < dss_default_size)
94 width = dss_default_size;
95
96 return getDSSURL(p, width, height, version, md);
97}
98
99QString KSDssDownloader::getDSSURL(const SkyPoint *const p, float width, float height, const QString &version,
100 struct KSDssImage::Metadata *md)
101{
102 Q_ASSERT(p);
103 if (!p)
104 return QString();
105 if (width <= 0)
106 return QString();
107 if (height <= 0)
108 height = width;
109 QString URL = getDSSURL(p->ra0(), p->dec0(), width, height, "gif", version, md);
110 if (md)
111 {
112 const SkyObject *obj = nullptr;
113 obj = dynamic_cast<const SkyObject *>(p);
114 if (obj && obj->hasName())
115 md->object = obj->name();
116 }
117 return URL;
118}
119
120QString KSDssDownloader::getDSSURL(const dms &ra, const dms &dec, float width, float height, const QString &type_,
121 const QString &version_, struct KSDssImage::Metadata *md)
122{
123 const double dss_default_size = Options::defaultDSSImageSize();
124
125 QString version = version_.toLower();
126 QString type = type_.toLower();
127 QString URLprefix = QString("https://archive.stsci.edu/cgi-bin/dss_search?v=%1&").arg(version);
128 QString URLsuffix = QString("&e=J2000&f=%1&c=none&fov=NONE").arg(type);
129
130 char decsgn = (dec.Degrees() < 0.0) ? '-' : '+';
131 int dd = abs(dec.degree());
132 int dm = abs(dec.arcmin());
133 int ds = abs(dec.arcsec());
134
135 // Infinite and NaN sizes are replaced by the default size and tiny DSS images are resized to default size
136 if (!qIsFinite(height) || height <= 0.0)
137 height = dss_default_size;
138 if (!qIsFinite(width) || width <= 0.0)
139 width = dss_default_size;
140
141 // DSS accepts images that are no larger than 75 arcminutes
142 if (height > 75.0)
143 height = 75.0;
144 if (width > 75.0)
145 width = 75.0;
146
148 DecString = QString::asprintf("&d=%c%02d+%02d+%02d", decsgn, dd, dm, ds);
149 RAString = QString::asprintf("r=%02d+%02d+%02d", ra.hour(), ra.minute(), ra.second());
150 SizeString = QString::asprintf("&h=%02.1f&w=%02.1f", height, width);
151
152 if (md)
153 {
154 md->src = KSDssImage::Metadata::DSS;
155 md->width = width;
156 md->height = height;
157 md->ra0 = ra;
158 md->dec0 = dec;
159 md->version = version;
160 md->format = (type.contains("fit") ? KSDssImage::Metadata::FITS : KSDssImage::Metadata::GIF);
161 md->height = height;
162 md->width = width;
163
164 if (version.contains("poss2"))
165 md->gen = 2;
166 else if (version.contains("poss1"))
167 md->gen = 1;
168 else if (version.contains("quickv"))
169 md->gen = 4;
170 else
171 md->gen = -1;
172
173 if (version.contains("red"))
174 md->band = 'R';
175 else if (version.contains("blue"))
176 md->band = 'B';
177 else if (version.contains("ir"))
178 md->band = 'I';
179 else if (version.contains("quickv"))
180 md->band = 'V';
181 else
182 md->band = '?';
183
184 md->object = QString();
185 }
186
188}
189
190void KSDssDownloader::initiateSingleDownloadAttempt(QUrl srcUrl)
191{
192 qDebug() << Q_FUNC_INFO << "Temp file is at " << m_TempFile.fileName();
193 QUrl fileUrl = QUrl::fromLocalFile(m_TempFile.fileName());
194 qDebug() << Q_FUNC_INFO << "Attempt #" << m_attempt << "downloading DSS Image. URL: " << srcUrl << " to " << fileUrl;
195 //m_DownloadJob = KIO::copy( srcUrl, fileUrl, KIO::Overwrite ) ; // FIXME: Can be done with pure Qt
196 //connect ( m_DownloadJob, SIGNAL (result(KJob*)), SLOT (downloadAttemptFinished()) );
197
198 downloadJob = new FileDownloader();
199
200 downloadJob->setProgressDialogEnabled(true, i18n("DSS Download"),
201 i18n("Please wait while DSS image is being downloaded..."));
202 connect(downloadJob, SIGNAL(canceled()), this, SIGNAL(downloadCanceled()));
203 connect(downloadJob, SIGNAL(downloaded()), this, SLOT(downloadAttemptFinished()));
204 connect(downloadJob, SIGNAL(error(QString)), this, SLOT(downloadError(QString)));
205
206 downloadJob->get(srcUrl);
207}
208
209void KSDssDownloader::startDownload(const SkyPoint *const p, const QString &destFileName)
210{
211 QUrl srcUrl;
212 m_FileName = destFileName;
213 m_attempt = 0;
214 srcUrl.setUrl(getDSSURL(p, m_VersionPreference[m_attempt], &m_AttemptData));
215 initiateSingleDownloadAttempt(srcUrl);
216}
217
219{
220 m_FileName = destFileName;
221 QUrl fileUrl = QUrl::fromLocalFile(m_TempFile.fileName());
222 qDebug() << Q_FUNC_INFO << "Downloading DSS Image from URL: " << srcUrl << " to " << fileUrl;
223 //m_DownloadJob = KIO::copy( srcUrl, fileUrl, KIO::Overwrite ) ; // FIXME: Can be done with pure Qt
224 //connect ( m_DownloadJob, SIGNAL (result(KJob*)), SLOT (singleDownloadFinished()) );
225
226 downloadJob = new FileDownloader();
227
228 downloadJob->setProgressDialogEnabled(true, i18n("DSS Download"),
229 i18n("Please wait while DSS image is being downloaded..."));
230 connect(downloadJob, SIGNAL(canceled()), this, SIGNAL(downloadCanceled()));
231 connect(downloadJob, SIGNAL(downloaded()), this, SLOT(singleDownloadFinished()));
232 connect(downloadJob, SIGNAL(error(QString)), this, SLOT(downloadError(QString)));
233
234 m_AttemptData = md;
235
236 downloadJob->get(srcUrl);
237}
238
239void KSDssDownloader::downloadError(const QString &errorString)
240{
241 qDebug() << Q_FUNC_INFO << "Error " << errorString << " downloading DSS images!";
242 emit downloadComplete(false);
243 downloadJob->deleteLater();
244}
245
246void KSDssDownloader::singleDownloadFinished()
247{
248 m_TempFile.open();
249 m_TempFile.write(downloadJob->downloadedData());
250 m_TempFile.close();
251 downloadJob->deleteLater();
252
253 // Check if we have a proper DSS image or the DSS server failed
255 QMimeType mt = mdb.mimeTypeForFile(m_TempFile.fileName(), QMimeDatabase::MatchContent);
256 if (mt.name().contains("image", Qt::CaseInsensitive))
257 {
258 qDebug() << Q_FUNC_INFO << "DSS download was successful";
259 emit downloadComplete(writeImageWithMetadata(m_TempFile.fileName(), m_FileName, m_AttemptData));
260 return;
261 }
262 else
263 emit downloadComplete(false);
264}
265
266void KSDssDownloader::downloadAttemptFinished()
267{
268 if (m_AttemptData.src == KSDssImage::Metadata::SDSS)
269 {
270 // FIXME: do SDSS-y things
271 emit downloadComplete(false);
272 deleteLater();
273 downloadJob->deleteLater();
274 return;
275 }
276 else
277 {
278 m_TempFile.open();
279 m_TempFile.write(downloadJob->downloadedData());
280 m_TempFile.close();
281 downloadJob->deleteLater();
282
283 // Check if we have a proper DSS image or the DSS server failed
285 QMimeType mt = mdb.mimeTypeForFile(m_TempFile.fileName(), QMimeDatabase::MatchContent);
286 if (mt.name().contains("image", Qt::CaseInsensitive))
287 {
288 qDebug() << Q_FUNC_INFO << "DSS download was successful";
289 emit downloadComplete(writeImageFile());
290 deleteLater();
291 return;
292 }
293
294 // We must have failed, try the next attempt
295 QUrl srcUrl;
296 m_attempt++;
297 if (m_attempt == m_VersionPreference.count())
298 {
299 // Nothing downloaded... very strange. Fail.
300 qDebug() << Q_FUNC_INFO << "Error downloading DSS images: All alternatives failed!";
301 emit downloadComplete(false);
302 deleteLater();
303 return;
304 }
305 srcUrl.setUrl(getDSSURL(m_AttemptData.ra0, m_AttemptData.dec0, m_AttemptData.width, m_AttemptData.height,
306 ((m_AttemptData.format == KSDssImage::Metadata::FITS) ? "fits" : "gif"),
307 m_VersionPreference[m_attempt], &m_AttemptData));
308 initiateSingleDownloadAttempt(srcUrl);
309 }
310}
311
312bool KSDssDownloader::writeImageFile()
313{
314 return writeImageWithMetadata(m_TempFile.fileName(), m_FileName, m_AttemptData);
315}
316
319{
320 // Write the temporary file into an image file with metadata
322 QImageWriter writer(destFile, "png");
323
324 writer.setText("Calibrated",
325 "true"); // This means that the image has RA/Dec size and orientation that is calibrated
326 writer.setText("PA", "0"); // Position Angle is zero degrees for DSS images
327 writer.setText("Source", QString::number(KSDssImage::Metadata::DSS));
328 writer.setText("Format", QString::number(KSDssImage::Metadata::PNG));
329 writer.setText("Version", md.version);
330 writer.setText("Object", md.object);
331 writer.setText("RA", md.ra0.toHMSString(true));
332 writer.setText("Dec", md.dec0.toDMSString(true, true));
333 writer.setText("Width", QString::number(md.width));
334 writer.setText("Height", QString::number(md.height));
335 writer.setText("Band", QString() + md.band);
336 writer.setText("Generation", QString::number(md.gen));
337 writer.setText("Author", "KStars KSDssDownloader");
338 return writer.write(img);
339}
A simple container object to hold the minimum information for a Deep Sky Object to be drawn on the sk...
KSDssDownloader(QObject *parent=nullptr)
Constructor.
void startSingleDownload(const QUrl srcUrl, const QString &destFileName, KSDssImage::Metadata &md)
Stateful single-download of a supplied URL.
static QString getDSSURL(const SkyPoint *const p, const QString &version="all", struct KSDssImage::Metadata *md=nullptr)
High-level method to create a URL to obtain a DSS image for a given SkyPoint.
static bool writeImageWithMetadata(const QString &srcFile, const QString &destFile, const KSDssImage::Metadata &md)
Write image metadata into file.
Provides all necessary information about an object in the sky: its coordinates, name(s),...
Definition skyobject.h:42
virtual QString name(void) const
Definition skyobject.h:145
The sky coordinates of a point in the sky.
Definition skypoint.h:45
const CachingDms & ra0() const
Definition skypoint.h:251
const CachingDms & dec0() const
Definition skypoint.h:257
An angle, stored as degrees, but expressible in many ways.
Definition dms.h:38
int second() const
Definition dms.cpp:231
int minute() const
Definition dms.cpp:221
int hour() const
Definition dms.h:147
static constexpr double DegToRad
DegToRad is a const static member equal to the number of radians in one degree (dms::PI/180....
Definition dms.h:390
QString i18n(const char *text, const TYPE &arg...)
virtual void close() override
void setText(const QString &key, const QString &text)
bool write(const QImage &image)
qint64 write(const QByteArray &data)
qsizetype count() const const
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
void deleteLater()
QString arg(Args &&... args) const const
QString asprintf(const char *cformat,...)
QString number(double n, char format, int precision)
CaseInsensitive
virtual QString fileName() const const override
QUrl fromLocalFile(const QString &localFile)
void setUrl(const QString &url, ParsingMode parsingMode)
Structure to hold some DSS image metadata.
Definition ksdssimage.h:36
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:19:01 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.