Kstars

fitslabel.cpp
1/*
2 SPDX-FileCopyrightText: 2003-2017 Jasem Mutlaq <mutlaqja@ikarustech.com>
3 SPDX-FileCopyrightText: 2016-2017 Robert Lancaster <rlancaste@gmail.com>
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
39FITSLabel::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
53 roiRB->setPalette(pal);
54 QToolTip::showText(QPoint(1, 1), "Move Once to show selection stats", this);
55}
56
57
58void FITSLabel::setSize(double w, double h)
59{
60 m_Width = w;
61 m_Height = h;
62 m_Size = w * h;
63}
64
65bool FITSLabel::getMouseButtonDown()
66{
67 return mouseButtonDown;
68}
69
70/**
71This method was added to make the panning function work.
72If the mouse button is released, it resets mouseButtonDown variable and the mouse cursor.
73 */
74void 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 // Only process the circle if relevant to Catalog Objects and a query is not already in progress
102 if (view->showObjects && !view->m_ImageData->getCatQueryInProgress() && e->modifiers () == Qt::ShiftModifier)
103 emit circleSelected(m_p1, m_p2);
104 }
105
106}
107
108void FITSLabel::leaveEvent(QEvent *e)
109{
110 Q_UNUSED(e)
111 view->updateMagnifyingGlass(-1, -1);
112 emit mouseOverPixel(-1, -1);
113}
114
115/**
116I added some things to the top of this method to allow panning and Scope slewing to function.
117If you are in the dragMouse mode and the mousebutton is pressed, The method checks the difference
118between the location of the last point stored and the current event point to see how the mouse has moved.
119Then it moves the scrollbars and thus the view to the right location.
120Then it stores the current point so next time it can do it again.
121 */
122void FITSLabel::mouseMoveEvent(QMouseEvent *e)
123{
124 const QSharedPointer<FITSData> &imageData = view->imageData();
125 if (imageData.isNull())
126 return;
127
128 float scale = (view->getCurrentZoom() / ZOOM_DEFAULT);
129
130 double x = round(e->x() / scale);
131 double y = round(e->y() / scale);
132
133 //Panning
134 if (e->modifiers() != Qt::ShiftModifier && view->getCursorMode() == FITSView::dragCursor && mouseButtonDown )
135 {
136 QPoint newPoint = e->globalPos();
137 int dx = newPoint.x() - lastMousePoint.x();
138 int dy = newPoint.y() - lastMousePoint.y();
139 view->horizontalScrollBar()->setValue(view->horizontalScrollBar()->value() - dx);
140 view->verticalScrollBar()->setValue(view->verticalScrollBar()->value() - dy);
141
142 lastMousePoint = newPoint;
143 }
144 if( e->buttons() & Qt::LeftButton && view->getCursorMode() == FITSView::dragCursor )
145 {
146 //Translation of ROI
147 if(isRoiSelected && !mouseButtonDown)
148 {
149
150 int xdiff = x - prevPoint.x();
151 int ydiff = y - prevPoint.y();
152 roiRB->setGeometry(roiRB->geometry().translated(round(xdiff * scale), round(ydiff * scale)));
153 prevPoint = QPoint(x, y);
154
155 QRect roiRaw = roiRB->geometry();
156 // Opting to update stats on the go is extremely laggy for large images, only update if small image
157 if(!view->isLargeImage())
158 {
159 emit rectangleSelected(roiRaw.topLeft() / prevscale, roiRaw.bottomRight() / prevscale, true);
160 updateROIToolTip(e->globalPos());
161 }
162 }
163 //Stretching of ROI
164 if(e->modifiers() == Qt::ShiftModifier && !isRoiSelected && view->isSelectionRectShown())
165 {
166 roiRB->setGeometry(QRect(m_p1 * scale, QPoint(x, y)*scale).normalized());
167
168 QRect roiRaw = roiRB->geometry();
169 // Opting to update stats on the go is extremely laggy for large images, only update if small image
170 if(!view->isLargeImage())
171 {
172 emit rectangleSelected(roiRaw.topLeft() / prevscale, roiRaw.bottomRight() / prevscale, true);
173 updateROIToolTip(e->globalPos());
174 }
175 }
176 }
177
178 uint8_t const *buffer = imageData->getImageBuffer();
179
180 if (buffer == nullptr)
181 return;
182
183 x = round(e->x() / scale);
184 y = round(e->y() / scale);
185
187 view->updateMagnifyingGlass(x, y);
188 else
189 view->updateMagnifyingGlass(-1, -1);
190
191 x = KSUtils::clamp(x, 1.0, m_Width);
192 y = KSUtils::clamp(y, 1.0, m_Height);
193
194 emit newStatus(QString("X:%1 Y:%2").arg(static_cast<int>(x)).arg(static_cast<int>(y)), FITS_POSITION);
195
196 // Range is 0 to dim-1 when accessing array
197 x -= 1;
198 y -= 1;
199
200 emit mouseOverPixel(x, y);
201
202 int index = y * m_Width + x;
203 QString stringValue;
204
205 switch (imageData->getStatistics().dataType)
206 {
207 case TBYTE:
208 stringValue = QLocale().toString(buffer[index]);
209 break;
210
211 case TSHORT:
212 stringValue = QLocale().toString((reinterpret_cast<int16_t const*>(buffer))[index]);
213 break;
214
215 case TUSHORT:
216 stringValue = QLocale().toString((reinterpret_cast<uint16_t const*>(buffer))[index]);
217 break;
218
219 case TLONG:
220 stringValue = QLocale().toString((reinterpret_cast<int32_t const*>(buffer))[index]);
221 break;
222
223 case TULONG:
224 stringValue = QLocale().toString((reinterpret_cast<uint32_t const*>(buffer))[index]);
225 break;
226
227 case TFLOAT:
228 stringValue = QLocale().toString((reinterpret_cast<float const*>(buffer))[index], 'f', 5);
229 break;
230
231 case TLONGLONG:
232 stringValue = QLocale().toString(static_cast<int>((reinterpret_cast<int64_t const*>(buffer))[index]));
233 break;
234
235 case TDOUBLE:
236 stringValue = QLocale().toString((reinterpret_cast<float const*>(buffer))[index], 'f', 5);
237
238 break;
239
240 default:
241 break;
242
243 }
244
245 if(view->isSelectionRectShown())
246 {
247 if (roiRB->geometry().contains(e->pos()))
248 updateROIToolTip(e->globalPos());
249 else
251 }
252
253 emit newStatus(stringValue, FITS_VALUE);
254
255 if (imageData->hasWCS() &&
256 !view->isSelectionRectShown() &&
257 view->getCursorMode() != FITSView::selectCursor)
258 {
259 QPointF wcsPixelPoint(x, y);
260 SkyPoint wcsCoord;
261 if(imageData->pixelToWCS(wcsPixelPoint, wcsCoord))
262 {
263 m_RA = wcsCoord.ra0();
264 m_DE = wcsCoord.dec0();
265 emit newStatus(QString("%1 , %2").arg(m_RA.toHMSString(), m_DE.toDMSString()), FITS_WCS);
266 }
267
268 bool objFound = false;
269 for (auto &listObject : imageData->getSkyObjects())
270 {
271 if ((std::abs(listObject->x() - x) < 5 / scale) && (std::abs(listObject->y() - y) < 5 / scale))
272 {
274 QToolTip::text() + '\n' + listObject->skyObject()->name() + '\n' + listObject->skyObject()->longname(), this);
275 objFound = true;
276 break;
277 }
278 }
279 if (!objFound && !view->isSelectionRectShown())
281 }
282
283 double HFR = view->imageData()->getHFR(x + 1, y + 1, scale);
284
285
286 if (HFR > 0)
287 {
288 QString tip = QToolTip::text();
289 // Don't i18n away HFR: because the RegExp below checks for HFR: to make sure there aren't duplicate strings added.
290 QString hfrStr = QString("HFR: %1").arg(HFR, 4, 'f', 2);
291 if (tip.isEmpty() || tip == hfrStr)
292 QToolTip::showText(e->globalPos(), hfrStr, this);
293 else
294 {
295 QRegularExpression hfrRegEx("HFR\\: \\d+\\.\\d\\d");
296 if (tip.contains(hfrRegEx))
297 QToolTip::showText(e->globalPos(), tip.replace(hfrRegEx, hfrStr), this);
298 else
299 QToolTip::showText(e->globalPos(), QToolTip::text() + '\n' + hfrStr, this);
300 }
301 }
302
303 e->accept();
304}
305
306/**
307I added some things to the top of this method to allow panning and Scope slewing to function.
308If in dragMouse mode, the Panning function works by storing the cursor position when the mouse was pressed and setting
309the mouseButtonDown variable to true.
310If 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
311do want to slew to the WCS coordinates associated with the click location. If so, it calls the centerTelescope function.
312 */
313
314void FITSLabel::mousePressEvent(QMouseEvent *e)
315{
316 float scale = (view->getCurrentZoom() / ZOOM_DEFAULT);
317
318 double x = round(e->x() / scale);
319 double y = round(e->y() / scale);
320
321 m_p1.setX(x);//record first point for selection Rectangle
322 m_p1.setY(y);
323 prevPoint = QPoint(x, y);
324 prevscale = scale;
325 x = KSUtils::clamp(x, 1.0, m_Width);
326 y = KSUtils::clamp(y, 1.0, m_Height);
327
328 if(e->buttons() & Qt::LeftButton && view->getCursorMode() == FITSView::dragCursor)
329 {
330 if(roiRB->geometry().contains(x * scale, y * scale))
331 isRoiSelected = true;
332 }
333
334 if (view->getCursorMode() == FITSView::dragCursor && !isRoiSelected)
335 {
336 mouseButtonDown = true;
337 lastMousePoint = e->globalPos();
338 view->updateMouseCursor();
339 }
340 else if (e->buttons() & Qt::LeftButton && view->getCursorMode() == FITSView::scopeCursor)
341 {
342#ifdef HAVE_INDI
343 const QSharedPointer<FITSData> &view_data = view->imageData();
344 if (view_data->hasWCS())
345 {
346 QPointF wcsPixelPoint(x, y);
347 SkyPoint wcsCoord;
348 if(view_data->pixelToWCS(wcsPixelPoint, wcsCoord))
349 {
350 auto ra = wcsCoord.ra0();
351 auto dec = wcsCoord.dec0();
353 nullptr,
354 "Slewing to Coordinates: \nRA: " + ra.toHMSString() +
355 "\nDec: " + dec.toDMSString(),
356 i18n("Continue Slew"), KStandardGuiItem::cont(),
357 KStandardGuiItem::cancel(), "continue_slew_warning"))
358 {
359 centerTelescope(ra.Hours(), dec.Degrees());
360 view->setCursorMode(view->lastMouseMode);
361 view->updateScopeButton();
362 }
363 }
364 }
365#endif
366 }
367
368
369#ifdef HAVE_INDI
370 const QSharedPointer<FITSData> &view_data = view->imageData();
371
372 if (e->buttons() & Qt::RightButton && view->getCursorMode() != FITSView::scopeCursor)
373 {
374 mouseReleaseEvent(e);
375 if (view_data->hasWCS())
376 {
377 for (auto &listObject : view_data->getSkyObjects())
378 {
379 if ((std::abs(listObject->x() - x) < 10 / scale) && (std::abs(listObject->y() - y) < 10 / scale))
380 {
381 SkyObject *object = listObject->skyObject();
382 KSPopupMenu *pmenu;
383 pmenu = new KSPopupMenu();
384 object->initPopupMenu(pmenu);
386 for (auto action : actions)
387 {
388 if (action->text().left(7) == "Starhop")
389 pmenu->removeAction(action);
390 if (action->text().left(7) == "Angular")
391 pmenu->removeAction(action);
392 if (action->text().left(8) == "Add flag")
393 pmenu->removeAction(action);
394 if (action->text().left(12) == "Attach Label")
395 pmenu->removeAction(action);
396 }
397 pmenu->popup(e->globalPos());
399 break;
400 }
401 }
402 }
403
404 if (fabs(view->markerCrosshair.x() - x) <= 15 && fabs(view->markerCrosshair.y() - y) <= 15)
405 emit markerSelected(0, 0);
406 }
407#endif
408
409 if (e->buttons() & Qt::LeftButton)
410 {
411 if (view->getCursorMode() == FITSView::selectCursor)
412 emit pointSelected(x, y);
413 else if (view->getCursorMode() == FITSView::crosshairCursor)
414 emit pointSelected(x + 5 / scale, y + 5 / scale);
415 else if (view->showObjects)
416 emit highlightSelected(x, y);
417 }
418}
419
420void FITSLabel::mouseDoubleClickEvent(QMouseEvent *e)
421{
422 double x, y;
423
424 x = round(e->x() / (view->getCurrentZoom() / ZOOM_DEFAULT));
425 y = round(e->y() / (view->getCurrentZoom() / ZOOM_DEFAULT));
426
427 x = KSUtils::clamp(x, 1.0, m_Width);
428 y = KSUtils::clamp(y, 1.0, m_Height);
429
430 emit markerSelected(x, y);
431
432 return;
433}
434
435void FITSLabel::centerTelescope(double raJ2000, double decJ2000)
436{
437#ifdef HAVE_INDI
438
439 if (INDIListener::Instance()->size() == 0)
440 {
441 KSNotification::sorry(i18n("KStars did not find any active mounts."));
442 return;
443 }
444
445 for (auto &oneDevice : INDIListener::devices())
446 {
447 if (!(oneDevice->getDriverInterface() & INDI::BaseDevice::TELESCOPE_INTERFACE))
448 continue;
449
450 if (oneDevice->isConnected() == false)
451 {
452 KSNotification::error(i18n("Mount %1 is offline. Please connect and retry again.", oneDevice->getDeviceName()));
453 return;
454 }
455
456 auto mount = oneDevice->getMount();
457 if (!mount)
458 continue;
459
460 SkyPoint selectedObject;
461 selectedObject.setRA0(raJ2000);
462 selectedObject.setDec0(decJ2000);
463 selectedObject.apparentCoord(J2000, KStarsData::Instance()->ut().djd());
464 mount->Slew(&selectedObject);
465 return;
466 }
467
468 KSNotification::sorry(i18n("KStars did not find any active mounts."));
469
470#else
471
472 Q_UNUSED(raJ2000);
473 Q_UNUSED(decJ2000);
474
475#endif
476}
477
478void FITSLabel::showRubberBand(bool on)
479{
480 if(on)
481 {
482 roiRB->show();
483 }
484 else
485 {
486 roiRB->hide();
487 }
488}
489
490/// Scales the rubberband on zoom
491void FITSLabel::zoomRubberBand(double scale)
492{
493 QRect r = roiRB->geometry() ;
494
495 if(prevscale == 0.0 )
496 prevscale = scale;
497
498 double ap = r.width() / r.height();
499 double ow = r.width() * scale / prevscale;
500 double oh = r.height() * scale / prevscale;
501
502 int rx, ry;
503 rx = round(r.topLeft().x() * scale / prevscale);
504 ry = round(r.topLeft().y() * scale / prevscale);
505 r.setTopLeft(QPoint(rx, ry));
506
507 rx = round(r.bottomRight().x() * scale / prevscale);
508 ry = round(r.bottomRight().y() * scale / prevscale);
509 r.setBottomRight(QPoint(rx, ry));
510
511 if (ap != r.width() / r.height())
512 {
513 r.setSize(QSize(ow, oh));
514 }
515
516 roiRB->setGeometry(r);
517 prevscale = scale;
518}
519/// Intended to take raw rect as input from FITSView context
520void FITSLabel::setRubberBand(QRect rect)
521{
522 float scale = (view->getCurrentZoom() / ZOOM_DEFAULT);
523
524 int rx, ry;
525 rx = round(rect.topLeft().x() * scale );
526 ry = round(rect.topLeft().y() * scale );
527 rect.setTopLeft(QPoint(rx, ry));
528
529 rx = round(rect.bottomRight().x() * scale );
530 ry = round(rect.bottomRight().y() * scale );
531 rect.setBottomRight(QPoint(rx, ry));
532
533 roiRB->setGeometry(rect);
534 prevscale = scale;
535}
536
537void FITSLabel::updateROIToolTip(const QPoint p)
538{
539 auto result = QString("σ %1").arg(QString::number(view->imageData()->getAverageStdDev(true), 'f', 2));
540 result += "\nx̄ " + QString::number(view->imageData()->getAverageMean(true), 'f', 2);
541 result += "\nM " + QString::number(view->imageData()->getAverageMedian(true), 'f', 2);
542 QToolTip::showText(p, result, this);
543}
The KStars Popup Menu.
Definition kspopupmenu.h:35
SkyMap * map() const
Definition kstars.h:139
static KStars * Instance()
Definition kstars.h:121
void setClickedObject(SkyObject *o)
Set the ClickedObject pointer to the argument.
Definition skymap.cpp:366
Provides all necessary information about an object in the sky: its coordinates, name(s),...
Definition skyobject.h:42
The sky coordinates of a point in the sky.
Definition skypoint.h:45
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 & ra0() const
Definition skypoint.h:251
void setRA0(dms r)
Sets RA0, the catalog Right Ascension.
Definition skypoint.h:94
const CachingDms & dec0() const
Definition skypoint.h:257
void setDec0(dms d)
Sets Dec0, the catalog Declination.
Definition skypoint.h:119
const QString toDMSString(const bool forceSign=false, const bool machineReadable=false, const bool highPrecision=false) const
Definition dms.cpp:287
const QString toHMSString(const bool machineReadable=false, const bool highPrecision=false) const
Definition dms.cpp:378
QString i18n(const char *text, const TYPE &arg...)
KIOCORE_EXPORT SimpleJob * mount(bool ro, const QByteArray &fstype, const QString &dev, const QString &point, JobFlags flags=DefaultFlags)
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)
KGuiItem cont()
KGuiItem cancel()
QScrollBar * horizontalScrollBar() const const
QScrollBar * verticalScrollBar() const const
void setValue(int)
void setAlphaF(float alpha)
void accept()
Qt::KeyboardModifiers queryKeyboardModifiers()
Qt::KeyboardModifiers modifiers() const const
QString toString(QDate date, FormatType format) const const
void popup(const QPoint &p, QAction *atAction)
QPoint globalPos() const const
QPoint pos() const const
int x() const const
int y() const const
void setBrush(ColorGroup group, ColorRole role, const QBrush &brush)
void setX(int x)
void setY(int y)
int x() const const
int y() const const
qreal x() const const
qreal y() const const
QPoint bottomRight() const const
int height() const const
void setBottomRight(const QPoint &position)
void setSize(const QSize &size)
void setTopLeft(const QPoint &position)
QPoint topLeft() const const
int width() const const
void setGeometry(const QRect &rect)
bool isNull() const const
Qt::MouseButtons buttons() const const
QString arg(Args &&... args) const const
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
QString number(double n, char format, int precision)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
ShiftModifier
LeftButton
WA_TransparentForMouseEvents
QTextStream & dec(QTextStream &stream)
void hideText()
void showText(const QPoint &pos, const QString &text, QWidget *w, const QRect &rect, int msecDisplayTime)
QString text()
QList< QAction * > actions() const const
void hide()
void removeAction(QAction *action)
void show()
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Dec 21 2024 17:04:46 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.