Kstars

eyepiecefield.cpp
1/*
2 SPDX-FileCopyrightText: 2014 Akarsh Simha <akarsh.simha@kdemail.net>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "eyepiecefield.h"
8
9#include "fov.h"
10#include "ksdssdownloader.h"
11#include "kstars.h"
12#include "Options.h"
13#include "skymap.h"
14#include "skyqpainter.h"
15
16#include <QBitmap>
17#include <QTemporaryFile>
18#include <QSvgGenerator>
19#include <QSvgRenderer>
20
21#include <kstars_debug.h>
22
23void EyepieceField::generateEyepieceView(SkyPoint *sp, QImage *skyChart, QImage *skyImage, const FOV *fov,
24 const QString &imagePath)
25{
26 if (fov)
27 {
28 generateEyepieceView(sp, skyChart, skyImage, fov->sizeX(), fov->sizeY(), imagePath);
29 }
30 else
31 {
32 generateEyepieceView(sp, skyChart, skyImage, -1.0, -1.0, imagePath);
33 }
34}
35
36void EyepieceField::generateEyepieceView(SkyPoint *sp, QImage *skyChart, QImage *skyImage, double fovWidth,
37 double fovHeight, const QString &imagePath)
38{
39 SkyMap *map = SkyMap::Instance();
41
42 Q_ASSERT(sp);
43 Q_ASSERT(map);
44 Q_ASSERT(ks);
45 Q_ASSERT(skyChart);
46
47 if (!skyChart)
48 return;
49
50 if (!map) // Requires initialization of Sky map.
51 return;
52
53 if (fovWidth <= 0)
54 {
55 if (!QFile::exists(imagePath))
56 return;
57 // Otherwise, we will assume that the user wants the FOV of the image and we'll try to guess it from there
58 }
59 if (fovHeight <= 0)
60 fovHeight = fovWidth;
61
62 // Get DSS image width / height
63 double dssWidth = 0, dssHeight = 0;
64
65 if (QFile::exists(imagePath))
66 {
67 KSDssImage dssImage(imagePath);
68 dssWidth = dssImage.getMetadata().width;
69 dssHeight = dssImage.getMetadata().height;
70 if (!dssImage.getMetadata().isValid() || dssWidth == 0 || dssHeight == 0)
71 {
72 // Metadata unavailable, guess based on most common DSS arcsec/pixel
73 //const double dssArcSecPerPixel = 1.01;
74 dssWidth = dssImage.getImage().width() * 1.01 / 60.0;
75 dssHeight = dssImage.getImage().height() * 1.01 / 60.0;
76 }
77 qCDebug(KSTARS) << "DSS width: " << dssWidth << " height: " << dssHeight;
78 }
79
80 // Set FOV width/height from DSS if necessary
81 if (fovWidth <= 0)
82 {
83 fovWidth = dssWidth;
84 fovHeight = dssHeight;
85 }
86
87 // Grab the sky chart
88 // Save the current state of the sky map
89 SkyPoint *oldFocus = map->focus();
90 double oldZoomFactor = Options::zoomFactor();
91
92 // Set the right zoom
93 ks->setApproxFOV(((fovWidth > fovHeight) ? fovWidth : fovHeight) / 15.0);
94
95 // map->setFocus( sp ); // FIXME: Why does setFocus() need a non-const SkyPoint pointer?
96 KStarsData *const data = KStarsData::Instance();
97 sp->updateCoords(data->updateNum(), true, data->geo()->lat(), data->lst(), false);
98 map->setClickedPoint(sp);
99 map->slotCenter();
100 qApp->processEvents();
101
102 // Repeat -- dirty workaround for some problem in KStars
103 map->setClickedPoint(sp);
104 map->slotCenter();
105 qApp->processEvents();
106
107 // determine screen arcminutes per pixel value
108 const double arcMinToScreen = dms::PI * Options::zoomFactor() / 10800.0;
109
110 // Vector export
111 QTemporaryFile myTempSvgFile;
112 myTempSvgFile.open();
113
114 // export as SVG
115 QSvgGenerator svgGenerator;
116 svgGenerator.setFileName(myTempSvgFile.fileName());
117 // svgGenerator.setTitle(i18n(""));
118 // svgGenerator.setDescription(i18n(""));
119 svgGenerator.setSize(QSize(map->width(), map->height()));
120 svgGenerator.setResolution(qMax(map->logicalDpiX(), map->logicalDpiY()));
121 svgGenerator.setViewBox(QRect(map->width() / 2.0 - arcMinToScreen * fovWidth / 2.0,
122 map->height() / 2.0 - arcMinToScreen * fovHeight / 2.0, arcMinToScreen * fovWidth,
123 arcMinToScreen * fovHeight));
124
125 SkyQPainter painter(KStars::Instance(), &svgGenerator);
126 painter.begin();
127
128 map->exportSkyImage(&painter);
129
130 painter.end();
131
132 // Render SVG file on raster QImage canvas
133 QSvgRenderer svgRenderer(myTempSvgFile.fileName());
134 QImage *mySkyChart = new QImage(arcMinToScreen * fovWidth * 2.0, arcMinToScreen * fovHeight * 2.0,
135 QImage::Format_ARGB32); // 2 times bigger in both dimensions.
136 QPainter p2(mySkyChart);
137 svgRenderer.render(&p2);
138 p2.end();
139 *skyChart = *mySkyChart;
140 delete mySkyChart;
141
142 myTempSvgFile.close();
143
144 // Reset the sky-map
145 map->setZoomFactor(oldZoomFactor);
146 map->setClickedPoint(oldFocus);
147 map->slotCenter();
148 qApp->processEvents();
149
150 // Repeat -- dirty workaround for some problem in KStars
151 map->setZoomFactor(oldZoomFactor);
152 map->setClickedPoint(oldFocus);
153 map->slotCenter();
154 qApp->processEvents();
155 map->forceUpdate();
156
157 // Prepare the sky image
158 if (QFile::exists(imagePath) && skyImage)
159 {
160 QImage *mySkyImage = new QImage(int(arcMinToScreen * fovWidth * 2.0), int(arcMinToScreen * fovHeight * 2.0),
162
163 mySkyImage->fill(Qt::transparent);
164
165 QPainter p(mySkyImage);
166 QImage rawImg(imagePath);
167
168 if (rawImg.isNull())
169 {
170 qWarning() << "Image constructed from " << imagePath
171 << "is a null image! Are you sure you supplied an image file? Continuing nevertheless...";
172 }
173
174 QImage img = rawImg.scaled(arcMinToScreen * dssWidth * 2.0, arcMinToScreen * dssHeight * 2.0,
176 const auto ksd = KStarsData::Instance();
177 sp->updateCoordsNow(ksd->updateNum());
178
179 if (Options::useAltAz())
180 {
181 // Need to rotate the image so that up is towards zenith rather than north.
182 sp->EquatorialToHorizontal(ksd->lst(), ksd->geo()->lat());
183 dms northBearing = findNorthAngle(sp, ksd->geo()->lat());
184 qCDebug(KSTARS) << "North angle = " << northBearing.toDMSString();
185
187
188 transform.rotate(northBearing.Degrees());
189 img = img.transformed(transform, Qt::SmoothTransformation);
190 }
191 p.drawImage(
192 QPointF(mySkyImage->width() / 2.0 - img.width() / 2.0, mySkyImage->height() / 2.0 - img.height() / 2.0),
193 img);
194 p.end();
195
196 *skyImage = *mySkyImage;
197 delete mySkyImage;
198 }
199}
200
201void EyepieceField::renderEyepieceView(const QImage *skyChart, QPixmap *renderChart, const double rotation,
202 const double scale, const bool flip, const bool invert, const QImage *skyImage,
203 QPixmap *renderImage, const bool overlay, const bool invertColors)
204{
206 bool deleteRenderImage = false;
207 transform.rotate(rotation);
208 if (flip)
209 transform.scale(-1, 1);
210 if (invert)
211 transform.scale(-1, -1);
212 transform.scale(scale, scale);
213
214 Q_ASSERT(skyChart && renderChart);
215 if (!skyChart || !renderChart)
216 return;
217
218 *renderChart = QPixmap::fromImage(skyChart->transformed(transform, Qt::SmoothTransformation));
219
220 if (skyImage)
221 {
222 Q_ASSERT(overlay || renderImage); // in debug mode, check for calls that supply skyImage but not renderImage
223 }
224 if (overlay && !renderImage)
225 {
226 renderImage = new QPixmap(); // temporary, used for rendering skymap before overlay is done.
227 deleteRenderImage = true; // we created it, so we must delete it.
228 }
229
230 if (skyImage && renderImage)
231 {
232 if (skyImage->isNull())
233 qWarning() << "Sky image supplied to renderEyepieceView() for rendering is a Null image!";
234 QImage i;
235 i = skyImage->transformed(transform, Qt::SmoothTransformation);
236 if (invertColors)
237 i.invertPixels();
238 *renderImage = QPixmap::fromImage(i);
239 }
240 if (overlay && skyImage)
241 {
242 QColor skyColor = KStarsData::Instance()->colorScheme()->colorNamed("SkyColor");
244 skyChart->createMaskFromColor(skyColor.rgb()).transformed(transform, Qt::SmoothTransformation));
245 renderChart->setMask(mask);
246 QPainter p(renderImage);
247 p.drawImage(QPointF(renderImage->width() / 2.0 - renderChart->width() / 2.0,
248 renderImage->height() / 2.0 - renderChart->height() / 2.0),
249 renderChart->toImage());
250 QPixmap temp(renderImage->width(), renderImage->height());
251 temp.fill(skyColor);
252 QPainter p2(&temp);
253 p2.drawImage(QPointF(0, 0), renderImage->toImage());
254 p2.end();
255 p.end();
256 *renderChart = *renderImage = temp;
257 }
258 if (deleteRenderImage)
259 delete renderImage;
260}
261
262void EyepieceField::renderEyepieceView(SkyPoint *sp, QPixmap *renderChart, double fovWidth, double fovHeight,
263 const double rotation, const double scale, const bool flip, const bool invert,
264 const QString &imagePath, QPixmap *renderImage, const bool overlay,
265 const bool invertColors)
266{
267 QImage *skyChart, *skyImage = nullptr;
268 skyChart = new QImage();
269 if (QFile::exists(imagePath) && (renderImage || overlay))
270 skyImage = new QImage();
271 generateEyepieceView(sp, skyChart, skyImage, fovWidth, fovHeight, imagePath);
272 renderEyepieceView(skyChart, renderChart, rotation, scale, flip, invert, skyImage, renderImage, overlay,
273 invertColors);
274 delete skyChart;
275 delete skyImage;
276}
277
278dms EyepieceField::findNorthAngle(const SkyPoint *sp, const dms *lat)
279{
280 Q_ASSERT(sp && lat);
281
282 // NOTE: northAngle1 is the correction due to lunisolar precession
283 // (needs testing and checking). northAngle2 is the correction due
284 // to going from equatorial to horizontal coordinates.
285
286 // FIXME: The following code is a guess at how to handle
287 // precession. While it might work in many cases, it might fail in
288 // some. Careful testing will be needed to ensure that all
289 // conditions are met, esp. with getting the signs right when
290 // using arccosine! Nutation and planetary precession corrections
291 // have not been included. -- asimha
292 // TODO: Look at the Meeus book and see if it has some formulas -- asimha
293 const double equinoxPrecessionPerYear =
294 (50.35 /
295 3600.0); // Equinox precession in ecliptic longitude per year in degrees (ref: http://star-www.st-and.ac.uk/~fv/webnotes/chapt16.htm)
296 dms netEquinoxPrecession(double((sp->getLastPrecessJD() - J2000) / 365.25) * equinoxPrecessionPerYear);
297 double cosNorthAngle1 =
298 (netEquinoxPrecession.cos() - sp->dec0().sin() * sp->dec().sin()) / (sp->dec0().cos() * sp->dec().cos());
299 double northAngle1 = acos(cosNorthAngle1);
300 if (sp->getLastPrecessJD() < J2000)
301 northAngle1 = -northAngle1;
302 if (sp->dec0().Degrees() < 0)
303 northAngle1 = -northAngle1;
304 // We trust that EquatorialToHorizontal has been called on sp, after all, how else can it have an alt/az representation.
305 // Use spherical cosine rule (the triangle with vertices at sp, zenith and NCP) to compute the angle between direction of increasing altitude and north
306 double cosNorthAngle2 = (lat->sin() - sp->alt().sin() * sp->dec().sin()) / (sp->alt().cos() * sp->dec().cos());
307 double northAngle2 = acos(cosNorthAngle2); // arccosine is blind to sign of the angle
308 if (sp->az().reduce().Degrees() < 180.0) // if on the eastern hemisphere, flip sign
309 northAngle2 = -northAngle2;
310 double northAngle = northAngle1 + northAngle2;
311 return dms(northAngle * 180 / M_PI);
312}
double cos() const
Get the cosine of this angle.
Definition cachingdms.h:204
double sin() const
Get the sine of this angle.
Definition cachingdms.h:190
QColor colorNamed(const QString &name) const
Retrieve a color by name.
A simple class encapsulating a Field-of-View symbol.
Definition fov.h:28
const CachingDms * lat() const
Definition geolocation.h:70
Provides a class to hold a DSS Image along with its metadata.
Definition ksdssimage.h:21
KStarsData is the backbone of KStars.
Definition kstarsdata.h:72
CachingDms * lst()
Definition kstarsdata.h:224
ColorScheme * colorScheme()
Definition kstarsdata.h:172
GeoLocation * geo()
Definition kstarsdata.h:230
This is the main window for KStars.
Definition kstars.h:89
static KStars * Instance()
Definition kstars.h:121
Q_SCRIPTABLE Q_NOREPLY void setApproxFOV(double FOV_Degrees)
DBUS interface function.
This is the canvas on which the sky is painted.
Definition skymap.h:54
The sky coordinates of a point in the sky.
Definition skypoint.h:45
const CachingDms & dec() const
Definition skypoint.h:269
virtual void updateCoordsNow(const KSNumbers *num)
updateCoordsNow Shortcut for updateCoords( const KSNumbers *num, false, nullptr, nullptr,...
Definition skypoint.h:391
long double getLastPrecessJD() const
Definition skypoint.h:294
void EquatorialToHorizontal(const CachingDms *LST, const CachingDms *lat)
Determine the (Altitude, Azimuth) coordinates of the SkyPoint from its (RA, Dec) coordinates,...
Definition skypoint.cpp:77
const dms & az() const
Definition skypoint.h:275
virtual void updateCoords(const KSNumbers *num, bool includePlanets=true, const CachingDms *lat=nullptr, const CachingDms *LST=nullptr, bool forceRecompute=false)
Determine the current coordinates (RA, Dec) from the catalog coordinates (RA0, Dec0),...
Definition skypoint.cpp:582
const dms & alt() const
Definition skypoint.h:281
const CachingDms & dec0() const
Definition skypoint.h:257
The QPainter-based painting backend.
Definition skyqpainter.h:31
An angle, stored as degrees, but expressible in many ways.
Definition dms.h:38
const dms reduce() const
return the equivalent angle between 0 and 360 degrees.
Definition dms.cpp:251
static constexpr double PI
PI is a const static member; it's public so that it can be used anywhere, as long as dms....
Definition dms.h:385
const QString toDMSString(const bool forceSign=false, const bool machineReadable=false, const bool highPrecision=false) const
Definition dms.cpp:287
const double & Degrees() const
Definition dms.h:141
double sin() const
Compute the Angle's Sine.
Definition dms.h:258
KDOCTOOLS_EXPORT QString transform(const QString &file, const QString &stylesheet, const QList< const char * > &params=QList< const char * >())
QBitmap fromImage(QImage &&image, Qt::ImageConversionFlags flags)
QRgb rgb() const const
bool exists() const const
virtual void close() override
const_iterator end() const const
QImage createMaskFromColor(QRgb color, Qt::MaskMode mode) const const
void fill(Qt::GlobalColor color)
int height() const const
void invertPixels(InvertMode mode)
bool isNull() const const
QImage scaled(const QSize &size, Qt::AspectRatioMode aspectRatioMode, Qt::TransformationMode transformMode) const const
QImage transformed(const QTransform &matrix, Qt::TransformationMode mode) const const
int width() const const
QPixmap fromImage(QImage &&image, Qt::ImageConversionFlags flags)
int height() const const
void setMask(const QBitmap &mask)
QImage toImage() const const
int width() const const
void setFileName(const QString &fileName)
void setResolution(int dpi)
void setSize(const QSize &size)
void setViewBox(const QRect &viewBox)
IgnoreAspectRatio
transparent
SmoothTransformation
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
virtual QString fileName() const const override
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 4 2024 16:38:44 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.