Kstars

equirectangularprojector.cpp
1/*
2 SPDX-FileCopyrightText: 2010 Henry de Valence <hdevalence@gmail.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "equirectangularprojector.h"
8
9#include "ksutils.h"
10#include "kstarsdata.h"
11#include "skycomponents/skylabeler.h"
12
13EquirectangularProjector::EquirectangularProjector(const ViewParams &p) : Projector(p)
14{
15 updateClipPoly();
16}
17
18Projector::Projection EquirectangularProjector::type() const
19{
20 return Equirectangular;
21}
22
24{
25 return 1.0;
26}
27
28Eigen::Vector2f EquirectangularProjector::toScreenVec(const SkyPoint *o, bool oRefract, bool *onVisibleHemisphere) const
29{
30 double Y, dX;
31 Eigen::Vector2f p;
32 double x, y;
33
34 oRefract &= m_vp.useRefraction;
35 if (m_vp.useAltAz)
36 {
37 double Y0;
38 Y = SkyPoint::refract(o->alt(), oRefract).radians(); //account for atmospheric refraction
39 Y0 = SkyPoint::refract(m_vp.focus->alt(), oRefract).radians();
40 dX = m_vp.focus->az().reduce().radians() - o->az().reduce().radians();
41
42 y = (Y - Y0);
43 }
44 else
45 {
46 dX = o->ra().reduce().radians() - m_vp.focus->ra().reduce().radians();
47 Y = o->dec().radians();
48 y = (Y - m_vp.focus->dec().radians());
49 }
50
51 dX = KSUtils::reduceAngle(dX, -dms::PI, dms::PI);
52
53 x = dX;
54
55 p = rst(x, y);
56
57 if (onVisibleHemisphere)
58 *onVisibleHemisphere = (p[0] > 0 && p[0] < m_vp.width);
59
60 return p;
61}
62
63SkyPoint EquirectangularProjector::fromScreen(const QPointF &p, dms *LST, const dms *lat, bool onlyAltAz) const
64{
65 SkyPoint result;
66
67 //Convert pixel position to x and y offsets in radians
68 auto p_ = derst(p.x(), p.y());
69 double dx = p_[0];
70 double dy = p_[1];
71
72 if (m_vp.useAltAz)
73 {
74 dms az, alt;
75 dx = -1.0 * dx; //Azimuth goes in opposite direction compared to RA
76 az.setRadians(dx + m_vp.focus->az().radians());
77 alt.setRadians(dy + SkyPoint::refract(m_vp.focus->alt(), m_vp.useRefraction).radians());
78 result.setAz(az.reduce());
79 if (m_vp.useRefraction)
80 alt = SkyPoint::unrefract(alt);
81 result.setAlt(alt);
82 if (!onlyAltAz)
83 result.HorizontalToEquatorial(LST, lat);
84 return result;
85 }
86 else
87 {
88 dms ra, dec;
89 ra.setRadians(dx + m_vp.focus->ra().radians());
90 dec.setRadians(dy + m_vp.focus->dec().radians());
91 result.set(ra.reduce(), dec);
92 result.EquatorialToHorizontal(LST, lat);
93 return result;
94 }
95}
96
98{
99 auto p_ = derst(p.x(), p.y());
100 double dx = p_[0];
101 double dy = p_[1];
102 return (dx * dx > M_PI * M_PI / 4.0) || (dy * dy > M_PI * M_PI / 4.0);
103}
104
105QVector<Eigen::Vector2f> EquirectangularProjector::groundPoly(SkyPoint *labelpoint, bool *drawLabel) const
106{
107 float x0 = m_vp.width / 2.;
108 if (m_vp.useAltAz)
109 {
110 float dX = M_PI;
111
112 // N.B. alt ranges from -π/2 to π/2, but the focus can be at
113 // either extreme, so the Y-range of the map is actually -π to
114 // π -- asimha
115 float dY = M_PI;
116
117 SkyPoint belowFocus;
118 belowFocus.setAz(m_vp.focus->az().Degrees());
119 belowFocus.setAlt(0.0);
120
121 // Compute the ends of the horizon line
122 Eigen::Vector2f obf = toScreenVec(&belowFocus, false);
123 auto obf_derst = derst(obf.x(), obf.y());
124 auto corner1 = rst(obf_derst[0] - dX,
125 obf_derst[1]);
126 auto corner2 = rst(obf_derst[0] + dX,
127 obf_derst[1]);
128
129 auto corner3 = rst(obf_derst[0] + dX,
130 -dY);
131 auto corner4 = rst(obf_derst[0] - dX,
132 -dY);
133
135 //Construct the ground polygon, which is a simple rectangle in this case
136 ground << corner1
137 << corner2;
138 if (m_vp.fillGround) {
139 ground << corner3
140 << corner4;
141 }
142
143 if (labelpoint)
144 {
145 auto pLabel_ = corner2 - 50. * (corner1 - corner2).normalized();
146 QPointF pLabel(pLabel_[0], pLabel_[1]);
147 KStarsData *data = KStarsData::Instance();
148 *labelpoint = fromScreen(pLabel, data->lst(), data->geo()->lat());
149 }
150 if (drawLabel)
151 *drawLabel = true;
152
153 return ground;
154 }
155 else
156 {
157 float dX = m_vp.zoomFactor * M_PI; // RA ranges from 0 to 2π, so half-length is π
158 float dY = m_vp.zoomFactor * M_PI;
160
161 static const QString horizonLabel = i18n("Horizon");
162 float marginLeft, marginRight, marginTop, marginBot;
163 SkyLabeler::Instance()->getMargins(horizonLabel, &marginLeft, &marginRight, &marginTop, &marginBot);
164
165 double daz = 180.;
166 double faz = m_vp.focus->az().Degrees();
167 double az1 = faz - daz;
168 double az2 = faz + daz;
169
170 bool inverted = ((m_vp.rotationAngle + 90.0_deg).reduce().Degrees() > 180.);
171 bool allGround = true;
172 bool allSky = true;
173
174 double inc = 1.0;
175 //Add points along horizon
176 std::vector<Eigen::Vector2f> groundPoints;
177 for (double az = az1; az <= az2 + inc; az += inc)
178 {
179 SkyPoint p = pointAt(az);
180 bool visible = false;
181 Eigen::Vector2f o = toScreenVec(&p, false, &visible);
182 if (visible)
183 {
184 groundPoints.push_back(o);
185 //Set the label point if this point is onscreen
186 if (labelpoint && o.x() < marginRight && o.y() > marginTop && o.y() < marginBot)
187 *labelpoint = p;
188
189 if (o.y() > 0.)
190 allGround = false;
191 if (o.y() < m_vp.height)
192 allSky = false;
193 }
194 }
195
196 if (inverted)
197 std::swap(allGround, allSky);
198
199 if (allSky)
200 {
201 if (drawLabel)
202 *drawLabel = false;
204 }
205
206 const Eigen::Vector2f slope {m_vp.rotationAngle.cos(), m_vp.rotationAngle.sin()};
207 std::sort(groundPoints.begin(), groundPoints.end(), [&](const Eigen::Vector2f & a,
208 const Eigen::Vector2f & b)
209 {
210 return a.dot(slope) < b.dot(slope);
211 });
212
213 for (auto point : groundPoints)
214 {
215 ground.append(point);
216 }
217
218 // if (allGround)
219 // {
220 // ground.clear();
221 // ground.append(Eigen::Vector2f(x0 - dX, y0 - dY));
222 // ground.append(Eigen::Vector2f(x0 + dX, y0 - dY));
223 // ground.append(Eigen::Vector2f(x0 + dX, y0 + dY));
224 // ground.append(Eigen::Vector2f(x0 - dX, y0 + dY));
225 // if (drawLabel)
226 // *drawLabel = false;
227 // return ground;
228 // }
229
230 if (labelpoint)
231 {
232 QPointF pLabel(x0 - dX - 50., ground.last().y());
233 KStarsData *data = KStarsData::Instance();
234 *labelpoint = fromScreen(pLabel, data->lst(), data->geo()->lat());
235 }
236 if (drawLabel)
237 *drawLabel = true;
238
239 const auto lat = KStarsData::Instance()->geo()->lat();
240 const Eigen::Vector2f perpendicular {-m_vp.rotationAngle.sin(), m_vp.rotationAngle.cos()};
241 const double sgn = (lat->Degrees() > 0 ? 1. : -1.);
242 if (m_vp.fillGround)
243 {
244 ground.append(groundPoints.back() + perpendicular * sgn * dY);
245 ground.append(groundPoints.front() + perpendicular * sgn * dY);
246 }
247 return ground;
248 }
249}
250
252{
253 m_clipPolygon.clear();
254
255 m_clipPolygon << QPointF(0, 0) << QPointF(m_vp.width, 0) << QPointF(m_vp.width, m_vp.height)
256 << QPointF(0, m_vp.height);
257}
SkyPoint fromScreen(const QPointF &p, dms *LST, const dms *lat, bool onlyAltAz=false) const override
Determine RA, Dec coordinates of the pixel at (dx, dy), which are the screen pixel coordinate offsets...
void updateClipPoly() override
updateClipPoly calculate the clipping polygen given the current FOV.
Projection type() const override
Return the type of this projection.
double radius() const override
Get the radius of this projection's sky circle.
Eigen::Vector2f toScreenVec(const SkyPoint *o, bool oRefract=true, bool *onVisibleHemisphere=nullptr) const override
Given the coordinates of the SkyPoint argument, determine the pixel coordinates in the SkyMap.
bool unusablePoint(const QPointF &p) const override
Check if the current point on screen is a valid point on the sky.
QVector< Eigen::Vector2f > groundPoly(SkyPoint *labelpoint=nullptr, bool *drawLabel=nullptr) const override
Get the ground polygon.
const CachingDms * lat() const
Definition geolocation.h:70
KStarsData is the backbone of KStars.
Definition kstarsdata.h:74
CachingDms * lst()
Definition kstarsdata.h:226
GeoLocation * geo()
Definition kstarsdata.h:232
The Projector class is the primary class that serves as an interface to handle projections.
Definition projector.h:58
Eigen::Vector2f derst(double x, double y) const
Transform screen (x, y) to projector (x, y) accounting for scale, rotation.
Definition projector.h:336
Eigen::Vector2f rst(double x, double y) const
Transform proj (x, y) to screen (x, y) accounting for scale and rotation.
Definition projector.h:314
static SkyPoint pointAt(double az)
Helper function for drawing ground.
Definition projector.cpp:29
void getMargins(const QString &text, float *left, float *right, float *top, float *bot)
sets four margins for help in keeping labels entirely on the screen.
static double refract(const double alt, bool conditional=true)
Apply refraction correction to altitude, depending on conditional.
static double unrefract(const double alt, bool conditional=true)
Remove refraction correction, depending on conditional.
This is just a container that holds information needed to do projections.
Definition projector.h:37
bool fillGround
If the ground is filled, then points below horizon are invisible.
Definition projector.h:44
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
QString i18n(const char *text, const TYPE &arg...)
void append(QList< T > &&value)
void clear()
T & last()
qreal x() const const
qreal y() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Mon Nov 18 2024 12:16:41 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.