Kstars

hipsmanager.cpp
1 /*
2  SPDX-FileCopyrightText: 2015-2017 Pavel Mraz
3 
4  SPDX-FileCopyrightText: 2017 Jasem Mutlaq
5 
6  SPDX-License-Identifier: GPL-2.0-or-later
7 */
8 
9 #include "hipsmanager.h"
10 
11 #include "auxiliary/kspaths.h"
12 #include "auxiliary/ksuserdb.h"
13 #include "kstars.h"
14 #include "kstarsdata.h"
15 #include "kstars_debug.h"
16 #include "Options.h"
17 #include "skymap.h"
18 
19 #include <KConfigDialog>
20 
21 #include <QTime>
22 #include <QHash>
23 #include <QNetworkDiskCache>
24 #include <QPainter>
25 
26 static QNetworkDiskCache *g_discCache = nullptr;
27 static UrlFileDownload *g_download = nullptr;
28 
29 static int qHash(const pixCacheKey_t &key, uint seed)
30 {
31  return qHash(QString("%1_%2_%3").arg(key.level).arg(key.pix).arg(key.uid), seed);
32 }
33 
34 
35 inline bool operator==(const pixCacheKey_t &k1, const pixCacheKey_t &k2)
36 {
37  return (k1.uid == k2.uid) && (k1.level == k2.level) && (k1.pix == k2.pix);
38 }
39 
40 HIPSManager * HIPSManager::_HIPSManager = nullptr;
41 
42 HIPSManager *HIPSManager::Instance()
43 {
44  if (_HIPSManager == nullptr)
45  {
46  _HIPSManager = new HIPSManager();
47 
48  if (Options::hIPSUseOfflineSource())
49  {
50  QDir hipsDirectory(Options::hIPSOfflinePath());
51  auto orders = hipsDirectory.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
52  HIPSManager::Instance()->setOfflineLevels(orders);
53  _HIPSManager->setCurrentSource("Offline");
54  }
55  }
56 
57  return _HIPSManager;
58 }
59 
60 HIPSManager::HIPSManager() : QObject(KStars::Instance())
61 {
62  if (g_discCache == nullptr)
63  {
64  g_discCache = new QNetworkDiskCache();
65  }
66 
67  if (g_download == nullptr)
68  {
69  g_download = new UrlFileDownload(this, g_discCache);
70 
71  connect(g_download, SIGNAL(sigDownloadDone(QNetworkReply::NetworkError, QByteArray &, pixCacheKey_t &)),
72  this, SLOT(slotDone(QNetworkReply::NetworkError, QByteArray &, pixCacheKey_t &)));
73  }
74 
75  g_discCache->setCacheDirectory(QDir(KSPaths::writableLocation(QStandardPaths::CacheLocation)).filePath("hips"));
76  qint64 net = Options::hIPSNetCache();
77  qint64 value = net * 1024 * 1024;
78  g_discCache->setMaximumCacheSize(Options::hIPSNetCache() * 1024 * 1024);
79  value = Options::hIPSMemoryCache() * 1024 * 1024;
80  m_cache.setMaxCost(Options::hIPSMemoryCache() * 1024 * 1024);
81 
82 
83 
84 }
85 
86 void HIPSManager::showSettings()
87 {
88  KConfigDialog *dialog = KConfigDialog::exists("hipssettings");
89  if (dialog == nullptr)
90  {
91  dialog = new KConfigDialog(KStars::Instance(), "hipssettings", Options::self());
92  connect(dialog->button(QDialogButtonBox::Apply), SIGNAL(clicked()), SLOT(slotApply()));
93  connect(dialog->button(QDialogButtonBox::Ok), SIGNAL(clicked()), SLOT(slotApply()));
94 
95  displaySettings.reset(new OpsHIPSDisplay());
96  KPageWidgetItem *page = dialog->addPage(displaySettings.get(), i18n("Display"));
97  page->setIcon(QIcon::fromTheme("computer"));
98 
99  cacheSettings.reset(new OpsHIPSCache());
100  page = dialog->addPage(cacheSettings.get(), i18n("Cache"));
101  page->setIcon(QIcon::fromTheme("preferences-web-browser-cache"));
102 
103  sourceSettings.reset(new OpsHIPS());
104  page = dialog->addPage(sourceSettings.get(), i18n("Sources"));
105  page->setIcon(QIcon::fromTheme("view-preview"));
106 
107  dialog->resize(800, 600);
108  }
109 
110  dialog->show();
111 }
112 
113 void HIPSManager::slotApply()
114 {
115  if (Options::hIPSUseOfflineSource())
116  {
117  QDir hipsDirectory(Options::hIPSOfflinePath());
118  auto orders = hipsDirectory.entryList(QDir::AllDirs | QDir::NoDotAndDotDot);
119  HIPSManager::Instance()->setOfflineLevels(orders);
120  _HIPSManager->setCurrentSource("Offline");
121  }
122 
123  readSources();
125  SkyMap::Instance()->forceUpdate();
126 }
127 
128 qint64 HIPSManager::getDiscCacheSize() const
129 {
130  return g_discCache->cacheSize();
131 }
132 
133 void HIPSManager::readSources()
134 {
135  KStarsData::Instance()->userdb()->GetAllHIPSSources(m_hipsSources);
136 
137  QString currentSourceTitle = Options::hIPSSource();
138 
139  setCurrentSource(currentSourceTitle);
140 }
141 
142 /*void HIPSManager::setParam(const hipsParams_t &param)
143 {
144  m_param = param;
145  m_uid = qHash(param.url);
146 }*/
147 
148 QImage *HIPSManager::getPix(bool allsky, int level, int pix, bool &freeImage)
149 {
150  if (Options::hIPSUseOfflineSource() == false && m_currentSource.isEmpty())
151  {
152  qCWarning(KSTARS) << "HIPS source not available!";
153  return nullptr;
154  }
155 
156  int origPix = pix;
157  freeImage = false;
158 
159  if (allsky)
160  {
161  level = 0;
162  pix = 0;
163  }
164 
165  pixCacheKey_t key;
166 
167  key.level = level;
168  key.pix = pix;
169  key.uid = m_uid;
170 
171  pixCacheItem_t *item = getCacheItem(key);
172 
173  if (m_downloadMap.contains(key))
174  {
175  // downloading
176 
177  // try render (level - 1) while downloading
178  key.level = level - 1;
179  key.pix = pix / 4;
180  pixCacheItem_t *item = getCacheItem(key);
181 
182  if (item != nullptr)
183  {
184  QImage *cacheImage = item->image;
185  int size = m_currentTileWidth >> 1;
186  int offset = cacheImage->width() / size;
187  QImage *image = cacheImage;
188 
189  int index[4] = {0, 2, 1, 3};
190 
191  int ox = index[pix % 4] % offset;
192  int oy = index[pix % 4] / offset;
193 
194  QImage *newImage = new QImage(image->copy(ox * size, oy * size, size, size));
195  freeImage = true;
196  return newImage;
197  }
198  return nullptr;
199  }
200 
201  if (item != nullptr)
202  {
203  QImage *cacheImage = item->image;
204 
205  Q_ASSERT(!item->image->isNull());
206 
207  if (allsky && cacheImage != nullptr)
208  {
209  // all sky
210  int size = 64;
211  int offset = cacheImage->width() / size;
212  QImage *image = cacheImage;
213 
214  int ox = origPix % offset;
215  int oy = origPix / offset;
216 
217  QImage *newImage = new QImage(image->copy(ox * size, oy * size, size, size));
218  freeImage = true;
219  return newImage;
220  }
221 
222  return cacheImage;
223  }
224 
225  QString path;
226 
227  if (!allsky)
228  {
229  int dir = (pix / 10000) * 10000;
230 
231  path = "/Norder" + QString::number(level) + "/Dir" + QString::number(dir) + "/Npix" + QString::number(pix) +
232  '.' + m_currentFormat;
233  }
234  else
235  {
236  path = "/Norder3/Allsky." + m_currentFormat;
237  }
238 
239  QUrl downloadURL(m_currentURL);
240  downloadURL.setPath(downloadURL.path() + path);
241  g_download->begin(downloadURL, key);
242  m_downloadMap.insert(key);
243 
244  return nullptr;
245 }
246 
247 
248 #if 0
249 bool HIPSManager::parseProperties(hipsParams_t *param, const QString &filename, const QString &url)
250 {
251  QFile f(filename);
252 
253  if (!f.open(QFile::ReadOnly | QFile::Text))
254  {
255  qDebug() << Q_FUNC_INFO << "nf" << f.fileName();
256  return false;
257  }
258 
260  QTextStream in(&f);
261  while (!in.atEnd())
262  {
263  QString line = in.readLine();
264 
265  int index = line.indexOf("=");
266  if (index > 0)
267  {
268  map[line.left(index).simplified()] = line.mid(index + 1).simplified();
269  }
270  }
271 
272  param->url = url;
273  qDebug() << Q_FUNC_INFO << url;
274 
275  int count = 0;
276  QString tmp;
277 
278  if (map.contains("obs_collection"))
279  {
280  param->name = map["obs_collection"];
281  count++;
282  }
283 
284  if (map.contains("hips_tile_width"))
285  {
286  param->tileWidth = map["hips_tile_width"].toInt();
287  count++;
288  }
289 
290  if (map.contains("hips_order"))
291  {
292  param->max_level = map["hips_order"].toInt();
293  count++;
294  }
295 
296  if (map.contains("hips_tile_format"))
297  {
298  tmp = map["hips_tile_format"];
299 
300  QStringList list = tmp.split(" ");
301 
302  if (list.contains("jpeg"))
303  {
304  param->imageExtension = "jpg";
305  count++;
306  }
307  else if (list.contains("png"))
308  {
309  param->imageExtension = "png";
310  count++;
311  }
312  }
313 
314  if (map.contains("hips_frame") || map.contains("ohips_frame"))
315  {
316  if (map.contains("hips_frame"))
317  tmp = map["hips_frame"];
318  else
319  tmp = map["ohips_frame"];
320 
321  if (tmp == "equatorial")
322  {
323  param->frame = HIPS_FRAME_EQT;
324  count++;
325  }
326  else if (tmp == "galactic")
327  {
328  param->frame = HIPS_FRAME_GAL;
329  count++;
330  }
331  }
332 
333  return count == 5; // all items have been loaded
334 }
335 #endif
336 
337 void HIPSManager::cancelAll()
338 {
339  g_download->abortAll();
340 }
341 
342 void HIPSManager::clearDiscCache()
343 {
344  g_discCache->clear();
345 }
346 
347 void HIPSManager::slotDone(QNetworkReply::NetworkError error, QByteArray &data, pixCacheKey_t &key)
348 {
349  if (error == QNetworkReply::NoError)
350  {
351  m_downloadMap.remove(key);
352 
353  auto *item = new pixCacheItem_t;
354 
355  item->image = new QImage();
356  if (item->image->loadFromData(data))
357  {
358  addToMemoryCache(key, item);
359 
360  //SkyMap::Instance()->forceUpdate();
361  }
362  else
363  {
364  delete item;
365  qCWarning(KSTARS) << "no image" << data;
366  }
367  }
368  else
369  {
371  {
372  m_downloadMap.remove(key);
373  }
374  else
375  {
376  auto *timer = new RemoveTimer();
377  timer->setKey(key);
378  connect(timer, SIGNAL(remove(pixCacheKey_t &)), this, SLOT(removeTimer(pixCacheKey_t &)));
379  }
380  }
381 }
382 
383 void HIPSManager::removeTimer(pixCacheKey_t &key)
384 {
385  m_downloadMap.remove(key);
386  sender()->deleteLater();
387  emit sigRepaint();
388 }
389 
390 PixCache *HIPSManager::getCache()
391 {
392  return &m_cache;
393 }
394 
395 void HIPSManager::addToMemoryCache(pixCacheKey_t &key, pixCacheItem_t *item)
396 {
397  Q_ASSERT(item);
398  Q_ASSERT(item->image);
399 
400 #if QT_VERSION >= QT_VERSION_CHECK(5,10,0)
401  int cost = item->image->sizeInBytes();
402 #else
403  int cost = item->image->byteCount();
404 #endif
405 
406  m_cache.add(key, item, cost);
407 }
408 
409 pixCacheItem_t *HIPSManager::getCacheItem(pixCacheKey_t &key)
410 {
411  return m_cache.get(key);
412 }
413 
414 bool HIPSManager::setCurrentSource(const QString &title)
415 {
416  if (title == "None")
417  {
418  Options::setShowHIPS(false);
419  Options::setHIPSSource(title);
420  m_currentSource.clear();
421  m_currentFormat.clear();
422  m_currentFrame = HIPS_OTHER_FRAME;
423  m_currentURL.clear();
424  m_currentOrder = 0;
425  m_currentTileWidth = 0;
426  m_uid = 0;
427  return true;
428  }
429  // Offline DSS
430  else if (Options::hIPSUseOfflineSource() || title == "Offline")
431  {
432  m_currentFormat = "jpg";
433  m_currentTileWidth = 512;
434  m_currentFrame = HIPS_EQUATORIAL_FRAME;
435  m_currentURL = QUrl(Options::hIPSOfflinePath());
436  m_currentURL.setScheme("file");
437  m_currentOrder = m_OfflineLevelsMap.lastKey();
438  m_uid = qHash(m_currentURL);
439  Options::setShowHIPS(true);
440  return true;
441  }
442 
443 
444  for (QMap<QString, QString> &source : m_hipsSources)
445  {
446  if (source.value("obs_title") == title)
447  {
448  m_currentSource = source;
449  m_currentFormat = source.value("hips_tile_format");
450  if (m_currentFormat.contains("jpeg"))
451  m_currentFormat = "jpg";
452  else if (m_currentFormat.contains("png"))
453  m_currentFormat = "png";
454  else
455  {
456  qCWarning(KSTARS) << "FITS HIPS images are not currently supported.";
457  return false;
458  }
459 
460  m_currentOrder = source.value("hips_order").toInt();
461  m_currentTileWidth = source.value("hips_tile_width").toInt();
462 
463  if (source.value("hips_frame") == "equatorial")
464  m_currentFrame = HIPS_EQUATORIAL_FRAME;
465  else if (source.value("hips_frame") == "galactic")
466  m_currentFrame = HIPS_GALACTIC_FRAME;
467  else
468  m_currentFrame = HIPS_OTHER_FRAME;
469 
470  m_currentURL = QUrl(source.value("hips_service_url"));
471  m_uid = qHash(m_currentURL);
472 
473  Options::setHIPSSource(title);
474  Options::setShowHIPS(true);
475 
476  return true;
477  }
478  }
479 
480  return false;
481 }
482 
483 // Extract which levels are available for offline use.
484 void HIPSManager::setOfflineLevels(const QStringList &value)
485 {
486  for (auto oneLevel : value)
487  {
488  if (oneLevel.startsWith("Norder"))
489  {
490  oneLevel.remove("Norder");
491  auto level = oneLevel.toUInt();
492  m_OfflineLevelsMap[level] = level;
493  }
494  }
495 
496  // Now let's map all the missing levels, if any
497  for (int i = 3; i < 9; i++)
498  {
499  // Find closest level
500  if (m_OfflineLevelsMap.contains(i) == false)
501  {
502  const auto keys = m_OfflineLevelsMap.keys();
503  const auto values = m_OfflineLevelsMap.values();
504  auto it = std::upper_bound(keys.constBegin(), keys.constEnd(), i);
505  if (it != keys.end())
506  m_OfflineLevelsMap[i] = *it;
507  else
508  m_OfflineLevelsMap[i] = values.last();
509 
510  }
511  }
512 }
513 
514 int HIPSManager::getUsableLevel(int level) const
515 {
516  return Options::hIPSUseOfflineSource() ? m_OfflineLevelsMap[level] : level;
517 }
518 
519 void RemoveTimer::setKey(const pixCacheKey_t &key)
520 {
521  m_key = key;
522 }
QString number(int n, int base)
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
virtual qint64 cacheSize() const const override
bool contains(const QString &str, Qt::CaseSensitivity cs) const const
QIcon fromTheme(const QString &name)
QStringView level(QStringView ifopt)
KSUserDB * userdb()
Definition: kstarsdata.h:214
KIOFILEWIDGETS_EXPORT QStringList list(const QString &fileClass)
QString simplified() const const
virtual void clear() override
void setCacheDirectory(const QString &cacheDir)
static KStars * Instance()
Definition: kstars.h:125
void setIcon(const QIcon &icon)
QByteArray & remove(int pos, int len)
bool operator==(const Qt3DRender::QGraphicsApiFilter &reference, const Qt3DRender::QGraphicsApiFilter &sample)
QString i18n(const char *text, const TYPE &arg...)
KPageWidgetItem * addPage(QWidget *page, const QString &itemName, const QString &pixmapName=QString(), const QString &header=QString(), bool manage=true)
bool remove(const QString &column, const QVariant &value)
KCALENDARCORE_EXPORT uint qHash(const KCalendarCore::Period &key)
void setMaximumCacheSize(qint64 size)
static KConfigDialog * exists(const QString &name)
int indexOf(QChar ch, int from, Qt::CaseSensitivity cs) const const
This is the main window for KStars. In addition to the GUI elements, the class contains the program c...
Definition: kstars.h:92
void show()
void resize(int w, int h)
void repopulateHIPS()
Load HIPS information and repopulate menu.
Definition: kstarsinit.cpp:723
KIOFILEWIDGETS_EXPORT QString dir(const QString &fileClass)
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QString left(int n) const const
void forceUpdate(bool now=false)
Recalculates the positions of objects in the sky, and then repaints the sky map.
Definition: skymap.cpp:1186
QPushButton * button(QDialogButtonBox::StandardButton which) const
QFuture< void > map(Sequence &sequence, MapFunctor function)
QString mid(int position, int n) const const
QVector< V > values(const QMultiHash< K, V > &c)
QImage copy(const QRect &rectangle) const const
int width() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Fri Aug 12 2022 04:00:54 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.