Kstars

fitslabel.cpp
1 /*
2  SPDX-FileCopyrightText: 2003-2017 Jasem Mutlaq <[email protected]>
3  SPDX-FileCopyrightText: 2016-2017 Robert Lancaster <[email protected]>
4 
5  SPDX-License-Identifier: GPL-2.0-or-later
6 */
7 
8 #include "fitslabel.h"
9 
10 #include "config-kstars.h"
11 
12 #include "fitsdata.h"
13 #include "fitsview.h"
14 #include "kspopupmenu.h"
15 #include "kstars.h"
16 #include "kstarsdata.h"
17 #include "Options.h"
18 #include "skymap.h"
19 #include "ksnotification.h"
20 #include <QGuiApplication>
21 
22 #ifdef HAVE_INDI
23 #include "basedevice.h"
24 #include "indi/indilistener.h"
25 #include "indi/indiconcretedevice.h"
26 #include "indi/indimount.h"
27 #endif
28 
29 #include <QScrollBar>
30 #include <QToolTip>
31 
32 #define BASE_OFFSET 50
33 #define ZOOM_DEFAULT 100.0
34 #define ZOOM_MIN 10
35 #define ZOOM_MAX 400
36 #define ZOOM_LOW_INCR 10
37 #define ZOOM_HIGH_INCR 50
38 
39 FITSLabel::FITSLabel(FITSView *View, QWidget *parent) : QLabel(parent)
40 {
41  this->view = View;
42  prevscale = 0.0;
43  //Rubber Band options
44  roiRB = new QRubberBand( QRubberBand::Rectangle, this);
45  roiRB->setAttribute(Qt::WA_TransparentForMouseEvents, 1);
46  roiRB->setGeometry(QRect(QPoint(1, 1), QPoint(100, 100)));
47 
48  QPalette pal;
49  QColor red70 = Qt::red;
50  red70.setAlphaF(0.7);
51 
52  pal.setBrush(QPalette::Highlight, QBrush(red70));
53  roiRB->setPalette(pal);
54  QToolTip::showText(QPoint(1, 1), "Move Once to show selection stats", this);
55 }
56 
57 
58 void FITSLabel::setSize(double w, double h)
59 {
60  m_Width = w;
61  m_Height = h;
62  m_Size = w * h;
63 }
64 
65 bool FITSLabel::getMouseButtonDown()
66 {
67  return mouseButtonDown;
68 }
69 
70 /**
71 This method was added to make the panning function work.
72 If the mouse button is released, it resets mouseButtonDown variable and the mouse cursor.
73  */
74 void FITSLabel::mouseReleaseEvent(QMouseEvent *e)
75 {
76  float scale = (view->getCurrentZoom() / ZOOM_DEFAULT);
77 
78  double x = round(e->x() / scale);
79  double y = round(e->y() / scale);
80 
81  m_p2.setX(x);//record second point for selection Rectangle
82  m_p2.setY(y);
83 
84  if (view->getCursorMode() == FITSView::dragCursor)
85  {
86  mouseButtonDown = false;
87  view->updateMouseCursor();
88  if( isRoiSelected && view->isSelectionRectShown())
89  {
90  QRect roiRaw = roiRB->geometry();
91  emit rectangleSelected(roiRaw.topLeft() / prevscale, roiRaw.bottomRight() / prevscale, true);
92  updateROIToolTip(e->globalPos());
93  }
94  if( e->modifiers () == Qt::ShiftModifier && view->isSelectionRectShown())
95  {
96  QRect roiRaw = roiRB->geometry();
97  emit rectangleSelected(roiRaw.topLeft() / prevscale, roiRaw.bottomRight() / prevscale, true);
98  updateROIToolTip(e->globalPos());
99  }
100  isRoiSelected = false;
101  }
102 
103 }
104 
105 void FITSLabel::leaveEvent(QEvent *e)
106 {
107  Q_UNUSED(e)
108  view->updateMagnifyingGlass(-1, -1);
109 }
110 
111 /**
112 I added some things to the top of this method to allow panning and Scope slewing to function.
113 If you are in the dragMouse mode and the mousebutton is pressed, The method checks the difference
114 between the location of the last point stored and the current event point to see how the mouse has moved.
115 Then it moves the scrollbars and thus the view to the right location.
116 Then it stores the current point so next time it can do it again.
117  */
118 void FITSLabel::mouseMoveEvent(QMouseEvent *e)
119 {
120  const QSharedPointer<FITSData> &imageData = view->imageData();
121  if (imageData.isNull())
122  return;
123 
124  float scale = (view->getCurrentZoom() / ZOOM_DEFAULT);
125 
126  double x = round(e->x() / scale);
127  double y = round(e->y() / scale);
128 
129  //Panning
130  if (e->modifiers() != Qt::ShiftModifier && view->getCursorMode() == FITSView::dragCursor && mouseButtonDown )
131  {
132  QPoint newPoint = e->globalPos();
133  int dx = newPoint.x() - lastMousePoint.x();
134  int dy = newPoint.y() - lastMousePoint.y();
135  view->horizontalScrollBar()->setValue(view->horizontalScrollBar()->value() - dx);
136  view->verticalScrollBar()->setValue(view->verticalScrollBar()->value() - dy);
137 
138  lastMousePoint = newPoint;
139  }
140  if( e->buttons() & Qt::LeftButton && view->getCursorMode() == FITSView::dragCursor )
141  {
142  //Translation of ROI
143  if(isRoiSelected && !mouseButtonDown)
144  {
145 
146  int xdiff = x - prevPoint.x();
147  int ydiff = y - prevPoint.y();
148  roiRB->setGeometry(roiRB->geometry().translated(round(xdiff * scale), round(ydiff * scale)));
149  prevPoint = QPoint(x, y);
150 
151  QRect roiRaw = roiRB->geometry();
152  // Opting to update stats on the go is extremely laggy for large images, only update if small image
153  if(!view->isLargeImage())
154  {
155  emit rectangleSelected(roiRaw.topLeft() / prevscale, roiRaw.bottomRight() / prevscale, true);
156  updateROIToolTip(e->globalPos());
157  }
158  }
159  //Stretching of ROI
160  if(e->modifiers() == Qt::ShiftModifier && !isRoiSelected && view->isSelectionRectShown())
161  {
162  roiRB->setGeometry(QRect(m_p1 * scale, QPoint(x, y)*scale).normalized());
163 
164  QRect roiRaw = roiRB->geometry();
165  // Opting to update stats on the go is extremely laggy for large images, only update if small image
166  if(!view->isLargeImage())
167  {
168  emit rectangleSelected(roiRaw.topLeft() / prevscale, roiRaw.bottomRight() / prevscale, true);
169  updateROIToolTip(e->globalPos());
170  }
171  }
172  }
173 
174  uint8_t const *buffer = imageData->getImageBuffer();
175 
176  if (buffer == nullptr)
177  return;
178 
179  x = round(e->x() / scale);
180  y = round(e->y() / scale);
181 
183  view->updateMagnifyingGlass(x, y);
184  else
185  view->updateMagnifyingGlass(-1, -1);
186 
187  x = KSUtils::clamp(x, 1.0, m_Width);
188  y = KSUtils::clamp(y, 1.0, m_Height);
189 
190  emit newStatus(QString("X:%1 Y:%2").arg(static_cast<int>(x)).arg(static_cast<int>(y)), FITS_POSITION);
191 
192  // Range is 0 to dim-1 when accessing array
193  x -= 1;
194  y -= 1;
195 
196  int index = y * m_Width + x;
197  QString stringValue;
198 
199  switch (imageData->getStatistics().dataType)
200  {
201  case TBYTE:
202  stringValue = QLocale().toString(buffer[index]);
203  break;
204 
205  case TSHORT:
206  stringValue = QLocale().toString((reinterpret_cast<int16_t const*>(buffer))[index]);
207  break;
208 
209  case TUSHORT:
210  stringValue = QLocale().toString((reinterpret_cast<uint16_t const*>(buffer))[index]);
211  break;
212 
213  case TLONG:
214  stringValue = QLocale().toString((reinterpret_cast<int32_t const*>(buffer))[index]);
215  break;
216 
217  case TULONG:
218  stringValue = QLocale().toString((reinterpret_cast<uint32_t const*>(buffer))[index]);
219  break;
220 
221  case TFLOAT:
222  stringValue = QLocale().toString((reinterpret_cast<float const*>(buffer))[index], 'f', 5);
223  break;
224 
225  case TLONGLONG:
226  stringValue = QLocale().toString(static_cast<int>((reinterpret_cast<int64_t const*>(buffer))[index]));
227  break;
228 
229  case TDOUBLE:
230  stringValue = QLocale().toString((reinterpret_cast<float const*>(buffer))[index], 'f', 5);
231 
232  break;
233 
234  default:
235  break;
236 
237  }
238 
239  if(view->isSelectionRectShown())
240  {
241  if (roiRB->geometry().contains(e->pos()))
242  updateROIToolTip(e->globalPos());
243  else
245  }
246 
247  emit newStatus(stringValue, FITS_VALUE);
248 
249  if (imageData->hasWCS() &&
250  !view->isSelectionRectShown() &&
251  view->getCursorMode() != FITSView::selectCursor)
252  {
253  QPointF wcsPixelPoint(x, y);
254  SkyPoint wcsCoord;
255  if(imageData->pixelToWCS(wcsPixelPoint, wcsCoord))
256  {
257  m_RA = wcsCoord.ra0();
258  m_DE = wcsCoord.dec0();
259  emit newStatus(QString("%1 , %2").arg(m_RA.toHMSString(), m_DE.toDMSString()), FITS_WCS);
260  }
261 
262  bool objFound = false;
263  for (auto &listObject : imageData->getSkyObjects())
264  {
265  if ((std::abs(listObject->x() - x) < 5 / scale) && (std::abs(listObject->y() - y) < 5 / scale))
266  {
268  QToolTip::text() + '\n' + listObject->skyObject()->name() + '\n' + listObject->skyObject()->longname(), this);
269  objFound = true;
270  break;
271  }
272  }
273  if (!objFound && !view->isSelectionRectShown())
275  }
276 
277  double HFR = view->imageData()->getHFR(x, y);
278 
279 
280  if (HFR > 0)
281  QToolTip::showText(e->globalPos(), QToolTip::text() + '\n' + i18nc("Half Flux Radius", "HFR: %1", QString::number(HFR, 'g',
282  3)), this);
283 
284  //setCursor(Qt::CrossCursor);
285 
286  e->accept();
287 }
288 
289 /**
290 I added some things to the top of this method to allow panning and Scope slewing to function.
291 If in dragMouse mode, the Panning function works by storing the cursor position when the mouse was pressed and setting
292 the mouseButtonDown variable to true.
293 If in ScopeMouse mode and the mouse is clicked, if there is WCS data and a scope is available, the method will verify that you actually
294 do want to slew to the WCS coordinates associated with the click location. If so, it calls the centerTelescope function.
295  */
296 
297 void FITSLabel::mousePressEvent(QMouseEvent *e)
298 {
299  float scale = (view->getCurrentZoom() / ZOOM_DEFAULT);
300 
301  double x = round(e->x() / scale);
302  double y = round(e->y() / scale);
303 
304  m_p1.setX(x);//record first point for selection Rectangle
305  m_p1.setY(y);
306  prevPoint = QPoint(x, y);
307  prevscale = scale;
308  x = KSUtils::clamp(x, 1.0, m_Width);
309  y = KSUtils::clamp(y, 1.0, m_Height);
310 
311  if(e->buttons() & Qt::LeftButton && view->getCursorMode() == FITSView::dragCursor)
312  {
313  if(roiRB->geometry().contains(x * scale, y * scale))
314  isRoiSelected = true;
315  }
316 
317  if (view->getCursorMode() == FITSView::dragCursor && !isRoiSelected)
318  {
319  mouseButtonDown = true;
320  lastMousePoint = e->globalPos();
321  view->updateMouseCursor();
322  }
323  else if (e->buttons() & Qt::LeftButton && view->getCursorMode() == FITSView::scopeCursor)
324  {
325 #ifdef HAVE_INDI
326  const QSharedPointer<FITSData> &view_data = view->imageData();
327  if (view_data->hasWCS())
328  {
329  QPointF wcsPixelPoint(x, y);
330  SkyPoint wcsCoord;
331  if(view_data->pixelToWCS(wcsPixelPoint, wcsCoord))
332  {
333  auto ra = wcsCoord.ra0();
334  auto dec = wcsCoord.dec0();
335  if (KMessageBox::Continue == KMessageBox::warningContinueCancel(
336  nullptr,
337  "Slewing to Coordinates: \nRA: " + ra.toHMSString() +
338  "\nDec: " + dec.toDMSString(),
339  i18n("Continue Slew"), KStandardGuiItem::cont(),
340  KStandardGuiItem::cancel(), "continue_slew_warning"))
341  {
342  centerTelescope(ra.Hours(), dec.Degrees());
343  view->setCursorMode(view->lastMouseMode);
344  view->updateScopeButton();
345  }
346  }
347  }
348 #endif
349  }
350 
351 
352 #ifdef HAVE_INDI
353  const QSharedPointer<FITSData> &view_data = view->imageData();
354 
355  if (e->buttons() & Qt::RightButton && view->getCursorMode() != FITSView::scopeCursor)
356  {
357  mouseReleaseEvent(e);
358  if (view_data->hasWCS())
359  {
360  for (auto &listObject : view_data->getSkyObjects())
361  {
362  if ((std::abs(listObject->x() - x) < 10 / scale) && (std::abs(listObject->y() - y) < 10 / scale))
363  {
364  SkyObject *object = listObject->skyObject();
365  KSPopupMenu *pmenu;
366  pmenu = new KSPopupMenu();
367  object->initPopupMenu(pmenu);
368  QList<QAction *> actions = pmenu->actions();
369  for (auto action : actions)
370  {
371  if (action->text().left(7) == "Starhop")
372  pmenu->removeAction(action);
373  if (action->text().left(7) == "Angular")
374  pmenu->removeAction(action);
375  if (action->text().left(8) == "Add flag")
376  pmenu->removeAction(action);
377  if (action->text().left(12) == "Attach Label")
378  pmenu->removeAction(action);
379  }
380  pmenu->popup(e->globalPos());
381  KStars::Instance()->map()->setClickedObject(object);
382  break;
383  }
384  }
385  }
386 
387  if (fabs(view->markerCrosshair.x() - x) <= 15 && fabs(view->markerCrosshair.y() - y) <= 15)
388  emit markerSelected(0, 0);
389  }
390 #endif
391 
392  if (e->buttons() & Qt::LeftButton)
393  {
394  if (view->getCursorMode() == FITSView::selectCursor)
395  emit pointSelected(x, y);
396  else if (view->getCursorMode() == FITSView::crosshairCursor)
397  emit pointSelected(x + 5 / scale, y + 5 / scale);
398  }
399 }
400 
401 void FITSLabel::mouseDoubleClickEvent(QMouseEvent *e)
402 {
403  double x, y;
404 
405  x = round(e->x() / (view->getCurrentZoom() / ZOOM_DEFAULT));
406  y = round(e->y() / (view->getCurrentZoom() / ZOOM_DEFAULT));
407 
408  x = KSUtils::clamp(x, 1.0, m_Width);
409  y = KSUtils::clamp(y, 1.0, m_Height);
410 
411  emit markerSelected(x, y);
412 
413  return;
414 }
415 
416 void FITSLabel::centerTelescope(double raJ2000, double decJ2000)
417 {
418 #ifdef HAVE_INDI
419 
420  if (INDIListener::Instance()->size() == 0)
421  {
422  KSNotification::sorry(i18n("KStars did not find any active mounts."));
423  return;
424  }
425 
426  for (auto &oneDevice : INDIListener::Instance()->getDevices())
427  {
428  if (!(oneDevice->getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE))
429  continue;
430 
431  if (oneDevice->isConnected() == false)
432  {
433  KSNotification::error(i18n("Mount %1 is offline. Please connect and retry again.", oneDevice->getDeviceName()));
434  return;
435  }
436 
437  auto mount = dynamic_cast<ISD::Mount *>(oneDevice->getConcreteDevice(INDI::BaseDevice::TELESCOPE_INTERFACE));
438  if (!mount)
439  continue;
440 
441  SkyPoint selectedObject;
442  selectedObject.setRA0(raJ2000);
443  selectedObject.setDec0(decJ2000);
444  selectedObject.apparentCoord(J2000, KStarsData::Instance()->ut().djd());
445  mount->Slew(&selectedObject);
446  return;
447 
448  }
449 
450  KSNotification::sorry(i18n("KStars did not find any active mounts."));
451 
452 #else
453 
454  Q_UNUSED(raJ2000);
455  Q_UNUSED(decJ2000);
456 
457 #endif
458 }
459 
460 void FITSLabel::showRubberBand(bool on)
461 {
462  if(on)
463  {
464  roiRB->show();
465  }
466  else
467  {
468  roiRB->hide();
469  }
470 }
471 
472 /// Scales the rubberband on zoom
473 void FITSLabel::zoomRubberBand(double scale)
474 {
475  QRect r = roiRB->geometry() ;
476 
477  if(prevscale == 0.0 )
478  prevscale = scale;
479 
480  double ap = r.width() / r.height();
481  double ow = r.width() * scale / prevscale;
482  double oh = r.height() * scale / prevscale;
483 
484  int rx, ry;
485  rx = round(r.topLeft().x() * scale / prevscale);
486  ry = round(r.topLeft().y() * scale / prevscale);
487  r.setTopLeft(QPoint(rx, ry));
488 
489  rx = round(r.bottomRight().x() * scale / prevscale);
490  ry = round(r.bottomRight().y() * scale / prevscale);
491  r.setBottomRight(QPoint(rx, ry));
492 
493  if (ap != r.width() / r.height())
494  {
495  r.setSize(QSize(ow, oh));
496  }
497 
498  roiRB->setGeometry(r);
499  prevscale = scale;
500 }
501 /// Intended to take raw rect as input from FITSView context
502 void FITSLabel::setRubberBand(QRect rect)
503 {
504  float scale = (view->getCurrentZoom() / ZOOM_DEFAULT);
505 
506  int rx, ry;
507  rx = round(rect.topLeft().x() * scale );
508  ry = round(rect.topLeft().y() * scale );
509  rect.setTopLeft(QPoint(rx, ry));
510 
511  rx = round(rect.bottomRight().x() * scale );
512  ry = round(rect.bottomRight().y() * scale );
513  rect.setBottomRight(QPoint(rx, ry));
514 
515  roiRB->setGeometry(rect);
516  prevscale = scale;
517 }
518 
519 void FITSLabel::updateROIToolTip(const QPoint p)
520 {
521  auto result = QString("σ %1").arg(QString::number(view->imageData()->getAverageStdDev(true), 'f', 2));
522  result += "\nx̄ " + QString::number(view->imageData()->getAverageMean(true), 'f', 2);
523  result += "\nM " + QString::number(view->imageData()->getAverageMedian(true), 'f', 2);
524  QToolTip::showText(p, result, this);
525 }
QPoint pos() const const
void showText(const QPoint &pos, const QString &text, QWidget *w)
void setSize(const QSize &size)
QList< QAction * > actions() const const
QPoint topLeft() const const
QString number(int n, int base)
bool isNull() const const
ButtonCode warningContinueCancel(QWidget *parent, const QString &text, const QString &title=QString(), const KGuiItem &buttonContinue=KStandardGuiItem::cont(), const KGuiItem &buttonCancel=KStandardGuiItem::cancel(), const QString &dontAskAgainName=QString(), Options options=Notify)
Stores dms coordinates for a point in the sky. for converting between coordinate systems.
Definition: skypoint.h:44
void setDec0(dms d)
Sets Dec0, the catalog Declination.
Definition: skypoint.h:119
QString text()
int width() const const
SkyMap * map() const
Definition: kstars.h:143
int x() const const
int y() const const
QPoint bottomRight() const const
LeftButton
void setRA0(dms r)
Sets RA0, the catalog Right Ascension.
Definition: skypoint.h:94
static KStars * Instance()
Definition: kstars.h:125
KGuiItem cancel()
KIOCORE_EXPORT SimpleJob * mount(bool ro, const QByteArray &fstype, const QString &dev, const QString &point, JobFlags flags=DefaultFlags)
QString i18n(const char *text, const TYPE &arg...)
void setAlphaF(qreal alpha)
Qt::MouseButtons buttons() const const
void setTopLeft(const QPoint &position)
void setBrush(QPalette::ColorRole role, const QBrush &brush)
int x() const const
int y() const const
QString toString(qlonglong i) const const
void setClickedObject(SkyObject *o)
Set the ClickedObject pointer to the argument.
Definition: skymap.cpp:331
QTextStream & dec(QTextStream &stream)
void setBottomRight(const QPoint &position)
void removeAction(QAction *action)
Qt::KeyboardModifiers modifiers() const const
void popup(const QPoint &p, QAction *atAction)
int height() const const
Qt::KeyboardModifiers queryKeyboardModifiers()
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
void apparentCoord(long double jd0, long double jdf)
Computes the apparent coordinates for this SkyPoint for any epoch, accounting for the effects of prec...
Definition: skypoint.cpp:700
const CachingDms & dec0() const
Definition: skypoint.h:257
QPoint globalPos() const const
const CachingDms & ra0() const
Definition: skypoint.h:251
void hideText()
KGuiItem cont()
QString i18nc(const char *context, const char *text, const TYPE &arg...)
ShiftModifier
Information about an object in the sky.
Definition: skyobject.h:41
WA_TransparentForMouseEvents
void accept()
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sun Aug 14 2022 04:13:56 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.