Kstars

ksdssdownloader.cpp
1 /*
2  SPDX-FileCopyrightText: 2016 Akarsh Simha <[email protected]>
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 
29 KSDssDownloader::KSDssDownloader(const SkyPoint *const p, const QString &destFileName,
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 
49 QString KSDssDownloader::getDSSURL(const SkyPoint *const p, const QString &version, struct KSDssImage::Metadata *md)
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 
99 QString 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 
120 QString 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 
147  QString RAString, DecString, SizeString;
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 
187  return (URLprefix + RAString + DecString + SizeString + URLsuffix);
188 }
189 
190 void 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 
209 void 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 
218 void KSDssDownloader::startSingleDownload(const QUrl srcUrl, const QString &destFileName, KSDssImage::Metadata &md)
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 
239 void 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 
246 void 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
254  QMimeDatabase mdb;
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 
266 void 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
284  QMimeDatabase mdb;
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 
312 bool KSDssDownloader::writeImageFile()
313 {
314  return writeImageWithMetadata(m_TempFile.fileName(), m_FileName, m_AttemptData);
315 }
316 
317 bool KSDssDownloader::writeImageWithMetadata(const QString &srcFile, const QString &destFile,
318  const KSDssImage::Metadata &md)
319 {
320  // Write the temporary file into an image file with metadata
321  QImage img(srcFile);
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 }
QString version
Used for DSS – Indicates which version of scans to pull.
Definition: ksdssimage.h:65
QString number(int n, int base)
void startSingleDownload(const QUrl srcUrl, const QString &destFileName, KSDssImage::Metadata &md)
Stateful single-download of a supplied URL.
CaseInsensitive
QString object
Name / identifier of the object. Added to metadata.
Definition: ksdssimage.h:67
Stores dms coordinates for a point in the sky. for converting between coordinate systems.
Definition: skypoint.h:44
int count(const T &value) const const
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
virtual QString name(void) const
Definition: skyobject.h:145
Structure to hold some DSS image metadata.
Definition: ksdssimage.h:35
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
const QString toHMSString(const bool machineReadable=false, const bool highPrecision=false) const
Definition: dms.cpp:370
void setText(const QString &key, const QString &text)
Source src
DSS / SDSS – source of the image.
Definition: ksdssimage.h:71
virtual QString fileName() const const override
dms ra0
Center RA (J2000.0)
Definition: ksdssimage.h:73
float a() const
FileFormat format
File format used.
Definition: ksdssimage.h:69
void deleteLater()
QString i18n(const char *text, const TYPE &arg...)
const QString toDMSString(const bool forceSign=false, const bool machineReadable=false, const bool highPrecision=false) const
Definition: dms.cpp:279
QUrl fromLocalFile(const QString &localFile)
float width
Width in arcminutes.
Definition: ksdssimage.h:79
int gen
Generation for DSS images, data release for SDSS; use -1 for unknown.
Definition: ksdssimage.h:83
virtual void close() override
float height
Height in arcminutes.
Definition: ksdssimage.h:77
int second() const
Definition: dms.cpp:231
An angle, stored as degrees, but expressible in many ways.
Definition: dms.h:37
QString toLower() const const
void setUrl(const QString &url, QUrl::ParsingMode parsingMode)
dms dec0
Center Dec (J2000.0)
Definition: ksdssimage.h:75
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
const CachingDms & dec0() const
Definition: skypoint.h:257
KSDssDownloader(QObject *parent=nullptr)
Constructor.
const CachingDms & ra0() const
Definition: skypoint.h:251
int hour() const
Definition: dms.h:147
QMimeType mimeTypeForFile(const QString &fileName, QMimeDatabase::MatchMode mode) const const
QString asprintf(const char *cformat,...)
int minute() const
Definition: dms.cpp:221
static bool writeImageWithMetadata(const QString &srcFile, const QString &destFile, const KSDssImage::Metadata &md)
Write image metadata into file.
A simple container object to hold the minimum information for a Deep Sky Object to be drawn on the sk...
Definition: catalogobject.h:40
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.
Information about an object in the sky.
Definition: skyobject.h:41
bool write(const QImage &image)
char band
Photometric band (UBVRI...) Use "?" for unknown.
Definition: ksdssimage.h:81
qint64 write(const char *data, qint64 maxSize)
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sun Oct 1 2023 04:02:40 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.