1 /*
2 * Stellarium
3 * Copyright (C) 2006 Johannes Gajdosik
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * as published by the Free Software Foundation; either version 2
8 * of the License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
18 */
19
20
21 #include "StelObject.hpp"
22 #include "StelObjectMgr.hpp"
23 #include "StelApp.hpp"
24 #include "StelCore.hpp"
25 #include "StelProjector.hpp"
26 #include "StelUtils.hpp"
27 #include "StelTranslator.hpp"
28 #include "StelSkyDrawer.hpp"
29 #include "RefractionExtinction.hpp"
30 #include "StelLocation.hpp"
31 #include "SolarSystem.hpp"
32 #include "StelModuleMgr.hpp"
33 #include "LandscapeMgr.hpp"
34 #include "planetsephems/sidereal_time.h"
35 #include "planetsephems/precession.h"
36
37 #include <QRegularExpression>
38 #include <QDebug>
39 #include <QSettings>
40
41 int StelObject::stelObjectPMetaTypeID = qRegisterMetaType<StelObjectP>();
42
getEquinoxEquatorialPos(const StelCore * core) const43 Vec3d StelObject::getEquinoxEquatorialPos(const StelCore* core) const
44 {
45 return core->j2000ToEquinoxEqu(getJ2000EquatorialPos(core), StelCore::RefractionOff);
46 }
47
getEquinoxEquatorialPosApparent(const StelCore * core) const48 Vec3d StelObject::getEquinoxEquatorialPosApparent(const StelCore* core) const
49 {
50 return core->j2000ToEquinoxEqu(getJ2000EquatorialPos(core), StelCore::RefractionOn);
51 }
52
getEquinoxEquatorialPosAuto(const StelCore * core) const53 Vec3d StelObject::getEquinoxEquatorialPosAuto(const StelCore* core) const
54 {
55 return core->j2000ToEquinoxEqu(getJ2000EquatorialPos(core), StelCore::RefractionAuto);
56 }
57
58
59 // Get observer local sidereal coordinate
getSiderealPosGeometric(const StelCore * core) const60 Vec3d StelObject::getSiderealPosGeometric(const StelCore* core) const
61 {
62 return Mat4d::zrotation(-core->getLocalSiderealTime())* getEquinoxEquatorialPos(core);
63 }
64
65 // Get observer local sidereal coordinates, deflected by refraction
getSiderealPosApparent(const StelCore * core) const66 Vec3d StelObject::getSiderealPosApparent(const StelCore* core) const
67 {
68 Vec3d v=getAltAzPosApparent(core); // These already come with refraction!
69 v = core->altAzToEquinoxEqu(v, StelCore::RefractionOff);
70 return Mat4d::zrotation(-core->getLocalSiderealTime())*v;
71 }
72
getAltAzPosGeometric(const StelCore * core) const73 Vec3d StelObject::getAltAzPosGeometric(const StelCore* core) const
74 {
75 return core->j2000ToAltAz(getJ2000EquatorialPos(core), StelCore::RefractionOff);
76 }
77
78 // Get observer-centered alt/az position
getAltAzPosApparent(const StelCore * core) const79 Vec3d StelObject::getAltAzPosApparent(const StelCore* core) const
80 {
81 return core->j2000ToAltAz(getJ2000EquatorialPos(core), StelCore::RefractionOn);
82 }
83
84 // Get observer-centered alt/az position
getAltAzPosAuto(const StelCore * core) const85 Vec3d StelObject::getAltAzPosAuto(const StelCore* core) const
86 {
87 return core->j2000ToAltAz(getJ2000EquatorialPos(core), StelCore::RefractionAuto);
88 }
89
90 // Get observer-centered galactic position
getGalacticPos(const StelCore * core) const91 Vec3d StelObject::getGalacticPos(const StelCore *core) const
92 {
93 return core->j2000ToGalactic(getJ2000EquatorialPos(core));
94 }
95
96 // Get observer-centered supergalactic position
getSupergalacticPos(const StelCore * core) const97 Vec3d StelObject::getSupergalacticPos(const StelCore *core) const
98 {
99 return core->j2000ToSupergalactic(getJ2000EquatorialPos(core));
100 }
101
102 // Get parallactic angle, which is the deviation between zenith angle and north angle.
103 // Meeus, Astronomical Algorithms, 2nd ed. (1998), p.98.
getParallacticAngle(const StelCore * core) const104 float StelObject::getParallacticAngle(const StelCore* core) const
105 {
106 const double phi=static_cast<double>(core->getCurrentLocation().latitude)*M_PI/180.0;
107 const Vec3d siderealPos=getSiderealPosApparent(core);
108 double delta, ha;
109 StelUtils::rectToSphe(&ha, &delta, siderealPos);
110 ha *= -1.0; // We must invert the orientation sense in case of sidereal positions!
111
112 // A rare condition! Object exactly in zenith, avoid undefined result.
113 if ((ha==0.0) && ((delta-phi)==0.0))
114 return 0.0f;
115 else
116 return static_cast<float>(atan2(sin(ha), tan(phi)*cos(delta)-sin(delta)*cos(ha)));
117 }
118
119 // Checking position an object above mathematical horizon for current location
isAboveHorizon(const StelCore * core) const120 bool StelObject::isAboveHorizon(const StelCore *core) const
121 {
122 float az, alt;
123 StelUtils::rectToSphe(&az, &alt, getAltAzPosAuto(core));
124 return (alt >= 0.f);
125 }
126
127 // Checking position an object above real horizon for current location
isAboveRealHorizon(const StelCore * core) const128 bool StelObject::isAboveRealHorizon(const StelCore *core) const
129 {
130 bool r = true;
131 LandscapeMgr* lmgr = GETSTELMODULE(LandscapeMgr);
132 if (lmgr->getFlagLandscape())
133 {
134 if (lmgr->getLandscapeOpacity(getAltAzPosAuto(core))>0.85f) // landscape displayed
135 r = false;
136 }
137 else
138 r = isAboveHorizon(core); // check object is below mathematical horizon
139
140 return r;
141 }
142
getVMagnitude(const StelCore * core) const143 float StelObject::getVMagnitude(const StelCore* core) const
144 {
145 Q_UNUSED(core)
146 return 99;
147 }
148
getRTSTime(const StelCore * core,const double altitude) const149 Vec4d StelObject::getRTSTime(const StelCore *core, const double altitude) const
150 {
151 const StelLocation loc=core->getCurrentLocation();
152 if (loc.name.contains("->")) // a spaceship
153 return Vec4d(0., 0., 0., -1000.);
154
155 //StelObjectMgr* omgr=GETSTELMODULE(StelObjectMgr);
156 double ho = 0.;
157 if (core->getSkyDrawer()->getFlagHasAtmosphere())
158 {
159 // canonical" refraction at horizon is -34'. Replace by pressure-dependent value here!
160 // This fixes 0-degree refraction. Not sure if we use refracted position throughout? Canonical ephemerides have -6/-12/-18 without refraction.
161 Refraction refraction=core->getSkyDrawer()->getRefraction();
162 Vec3d zeroAlt(1.0,0.0,0.0);
163 refraction.backward(zeroAlt);
164 ho += asin(zeroAlt[2]);
165 }
166 if (altitude != 0.)
167 ho = altitude*M_PI_180; // Not sure if we use refraction for off-zero settings?
168 const double phi = static_cast<double>(loc.latitude) * M_PI_180;
169 const double L = static_cast<double>(loc.longitude) * M_PI_180; // OUR longitude. Meeus has it reversed
170 PlanetP obsPlanet = core->getCurrentPlanet();
171 const double rotRate = obsPlanet->getSiderealDay();
172 const double currentJD=core->getJD();
173 const double currentJDE=core->getJDE();
174
175 // And convert to equatorial coordinates of date. We can also use this day's current aberration, given the other uncertainties/omissions.
176 const Vec3d eq_2=getEquinoxEquatorialPos(core);
177 double ra2, de2;
178 StelUtils::rectToSphe(&ra2, &de2, eq_2);
179 // Around ra~12 there may be a jump between 12h and -12h which could crash interpolation. We better make sure to have either negative RA or RA>24 in this case.
180 if (cos(ra2)<0.)
181 {
182 ra2=StelUtils::fmodpos(ra2, 2*M_PI);
183 }
184
185 // 3. Approximate times:
186 // we use Sidereal Time of Place!
187 const double Theta2=obsPlanet->getSiderealTime(currentJD, currentJDE) * (M_PI/180.) + L; // [radians]
188 double cosH0=(sin(ho)-sin(phi)*sin(de2))/(cos(phi)*cos(de2));
189
190 //omgr->removeExtraInfoStrings(StelObject::DebugAid);
191 //omgr->addToExtraInfoString(StelObject::DebugAid, QString("α<sub>2</sub>: %1=%2 δ<sub>2</sub>: %3<br/>").arg(QString::number(ra2, 'f', 4)).arg(StelUtils::radToHmsStr(ra2)).arg(StelUtils::radToDmsStr(de2)));
192 //omgr->addToExtraInfoString(StelObject::DebugAid, QString("h<sub>0</sub>= %1<br/>").arg(StelUtils::radToDmsStr(ho)));
193 //omgr->addToExtraInfoString(StelObject::DebugAid, QString("JD<sub>2</sub>= %1<br/>").arg(QString::number(currentJD, 'f', 5)));
194 //omgr->addToExtraInfoString(StelObject::DebugAid, QString("Θ<sub>2</sub>= %1<br/>").arg(StelUtils::radToHmsStr(Theta2)));
195 //omgr->addToExtraInfoString(StelObject::DebugAid, QString("cos H<sub>0</sub>= %1<br/>").arg(QString::number(cosH0, 'f', 4)));
196
197
198 double h2=StelUtils::fmodpos(Theta2-ra2, 2.*M_PI); if (h2>M_PI) h2-=2.*M_PI; // Hour angle at currentJD. This should be [-pi, pi]
199 // Find approximation of transit time
200 //double JDt=currentJD-h2/(M_PI*2.)*rotRate;
201 //omgr->addToExtraInfoString(StelObject::DebugAid, QString("h<sub>2</sub>= %1<br/>").arg(QString::number(h2, 'f', 4)));
202 //omgr->addToExtraInfoString(StelObject::DebugAid, QString("JD<sub>t</sub>= %1<br/>").arg(QString::number(JDt, 'f', 4)));
203
204 // In terms of chapter 15, where m0, m1 and m2 are fractions of day within the current day, we use mr, mt, ms as fractions of day from currentJD, and they lie within [-1...+1].
205
206 double mr, ms, flag=0.;
207 double mt=-h2*(0.5*rotRate/M_PI);
208
209 // circumpolar: set rise and set times to lower culmination, i.e. 1/2 rotation from transit
210 if (fabs(cosH0)>1.)
211 {
212 flag = (cosH0<-1.) ? 100 : -100; // circumpolar / never rises
213 mr = (cosH0<-1.) ? mt-0.5*rotRate : mt;
214 ms = (cosH0<-1.) ? mt+0.5*rotRate : mt;
215 }
216 else
217 {
218 const double H0 = acos(cosH0);
219 //omgr->addToExtraInfoString(StelObject::DebugAid, QString("H<sub>0</sub>= %1<br/>").arg(QString::number(H0*M_180_PI, 'f', 6)));
220
221 mr = mt - H0*rotRate/(2.*M_PI);
222 ms = mt + H0*rotRate/(2.*M_PI);
223 }
224
225 //omgr->addToExtraInfoString(StelObject::DebugAid, QString("m<sub>t</sub>= %1<br/>").arg(QString::number(mt, 'f', 6)));
226 //omgr->addToExtraInfoString(StelObject::DebugAid, QString("m<sub>r</sub>= %1<br/>").arg(QString::number(mr, 'f', 6)));
227 //omgr->addToExtraInfoString(StelObject::DebugAid, QString("m<sub>s</sub>= %1<br/>").arg(QString::number(ms, 'f', 6)));
228
229 // Not quite done! Do the final tweaks...
230 if (fabs(cosH0)<1.)
231 {
232 // RISE
233 int iterations=0; // add this to limit the loops, just in case.
234 double Delta_mr=1.;
235 while (Delta_mr > 1./8640.) // Do that until accurate to 10 seconds
236 {
237 const double theta_mr=obsPlanet->getSiderealTime(currentJD+mr, currentJDE+mr) * (M_PI/180.) + L; // [radians]
238 double hr=StelUtils::fmodpos(theta_mr-ra2, 2.*M_PI); if (hr>M_PI) hr-=2.*M_PI; // Hour angle of the rising RA at currentJD. This should be [-pi, pi]
239 //omgr->addToExtraInfoString(StelObject::DebugAid, QString("h<sub>r</sub>': %1 = %2<br/>").arg(QString::number(hr, 'f', 6)).arg(StelUtils::radToHmsStr(hr, true)));
240
241 double ar=asin(sin(phi)*sin(de2)+cos(phi)*cos(de2)*cos(hr)); // altitude at this hour angle
242
243 Delta_mr= (ar-ho)/(cos(de2)*cos(phi)*sin(hr)) / (M_PI*2.);
244 Delta_mr=StelUtils::fmodpos(Delta_mr+0.5, 1.0)-0.5; // ensure this is a small correction
245 mr+=Delta_mr;
246
247 //omgr->addToExtraInfoString(StelObject::DebugAid, QString("alt<sub>r</sub>': %1 = %2<br/>").arg(QString::number(ar, 'f', 6)).arg(StelUtils::radToDmsStr(ar)));
248 //omgr->addToExtraInfoString(StelObject::DebugAid, QString("Δ<sub>mr</sub>'= %1<br/>").arg(QString::number(Delta_mr, 'f', 6)));
249 //omgr->addToExtraInfoString(StelObject::DebugAid, QString("m<sub>r</sub>' = %1<br/>").arg(QString::number(mr, 'f', 6)));
250
251 if (++iterations >= 5)
252 break;
253 }
254 // SET
255 iterations=0; // add this to limit the loops, just in case.
256 double Delta_ms=1.;
257 while (Delta_ms > 1./8640.) // Do that until accurate to 10 seconds
258 {
259 const double theta_ms=obsPlanet->getSiderealTime(currentJD+ms, currentJDE+ms) * (M_PI/180.) + L; // [radians]
260 double hs=StelUtils::fmodpos(theta_ms-ra2, 2.*M_PI); if (hs>M_PI) hs-=2.*M_PI; // Hour angle of the setting RA at currentJD. This should be [-pi, pi]
261 //omgr->addToExtraInfoString(StelObject::DebugAid, QString("h<sub>s</sub>': %1 = %2<br/>").arg(QString::number(hs, 'f', 6)).arg(StelUtils::radToHmsStr(hs, true)));
262
263 double as=asin(sin(phi)*sin(de2)+cos(phi)*cos(de2)*cos(hs)); // altitude at this hour angle
264
265 Delta_ms= (as-ho)/(cos(de2)*cos(phi)*sin(hs)) / (M_PI*2.);
266 Delta_ms=StelUtils::fmodpos(Delta_ms+0.5, 1.0)-0.5; // ensure this is a small correction
267 ms+=Delta_ms;
268
269 //omgr->addToExtraInfoString(StelObject::DebugAid, QString("alt<sub>s</sub>': %1 = %2<br/>").arg(QString::number(as, 'f', 6)).arg(StelUtils::radToDmsStr(as)));
270 //omgr->addToExtraInfoString(StelObject::DebugAid, QString("Δ<sub>ms</sub>'= %1<br/>").arg(QString::number(Delta_ms, 'f', 6)));
271 //omgr->addToExtraInfoString(StelObject::DebugAid, QString("m<sub>s</sub>' = %1<br/>").arg(QString::number(ms, 'f', 6)));
272
273 if (++iterations >= 5)
274 break;
275 }
276 }
277 return Vec4d(currentJD+mr, currentJD+mt, currentJD+ms, flag);
278 }
279
getSelectPriority(const StelCore * core) const280 float StelObject::getSelectPriority(const StelCore* core) const
281 {
282 return qMin(getVMagnitudeWithExtinction(core), 15.0f);
283 }
284
getVMagnitudeWithExtinction(const StelCore * core) const285 float StelObject::getVMagnitudeWithExtinction(const StelCore* core) const
286 {
287 Vec3d altAzPos = getAltAzPosGeometric(core);
288 altAzPos.normalize();
289 float vMag = getVMagnitude(core);
290 // without the test, planets flicker stupidly in fullsky atmosphere-less view.
291 if (core->getSkyDrawer()->getFlagHasAtmosphere())
292 core->getSkyDrawer()->getExtinction().forward(altAzPos, &vMag);
293 return vMag;
294 }
295
getAirmass(const StelCore * core) const296 float StelObject::getAirmass(const StelCore *core) const
297 {
298 double az_app, alt_app;
299 StelUtils::rectToSphe(&az_app, &alt_app, getAltAzPosApparent(core));
300 Q_UNUSED(az_app)
301 if (core->getSkyDrawer()->getFlagHasAtmosphere() && (alt_app>-2.0*M_PI_180)) // Don't compute extinction much below horizon where model is meaningless.
302 {
303 const Extinction &extinction=core->getSkyDrawer()->getExtinction();
304 return extinction.airmass(static_cast<float>(std::cos(M_PI_2-alt_app)), true);
305 }
306 else
307 return -1.f;
308 }
309
310 // Format the magnitude info string for the object
getMagnitudeInfoString(const StelCore * core,const InfoStringGroup & flags,const int decimals) const311 QString StelObject::getMagnitudeInfoString(const StelCore *core, const InfoStringGroup& flags, const int decimals) const
312 {
313 if (flags&Magnitude)
314 {
315 QString str = QString("%1: <b>%2</b>").arg(q_("Magnitude"), QString::number(getVMagnitude(core), 'f', decimals));
316 const float airmass = getAirmass(core);
317 if (airmass>-1.f) // Don't show extincted magnitude much below horizon where model is meaningless.
318 str += QString(" (%1 <b>%2</b> %3 <b>%4</b> %5)").arg(q_("reduced to"), QString::number(getVMagnitudeWithExtinction(core), 'f', decimals), q_("by"), QString::number(airmass, 'f', 2), q_("Airmasses"));
319 str += "<br/>" + getExtraInfoStrings(Magnitude).join("");
320 return str;
321 }
322 else
323 return QString();
324 }
325
326 // Format the positional info string contain J2000/of date/altaz/hour angle positions for the object
getCommonInfoString(const StelCore * core,const InfoStringGroup & flags) const327 QString StelObject::getCommonInfoString(const StelCore *core, const InfoStringGroup& flags) const
328 {
329 StelApp& app = StelApp::getInstance();
330 StelObjectMgr* omgr=GETSTELMODULE(StelObjectMgr);
331 const bool withAtmosphere = core->getSkyDrawer()->getFlagHasAtmosphere();
332 const bool withDecimalDegree = app.getFlagShowDecimalDegrees();
333 const bool useSouthAzimuth = app.getFlagSouthAzimuthUsage();
334 const bool withTables = app.getFlagUseFormattingOutput();
335 const bool withDesignations = app.getFlagUseCCSDesignation();
336 const QString cepoch = qc_("on date", "coordinates for current epoch");
337 const QString currentPlanet = core->getCurrentPlanet()->getEnglishName();
338 const QString apparent = " " + (withAtmosphere ? q_("(apparent)") : "");
339 QString res, firstCoordinate, secondCoordinate;
340 double az_app, alt_app;
341 StelUtils::rectToSphe(&az_app,&alt_app,getAltAzPosApparent(core));
342 Q_UNUSED(az_app)
343
344 if (withTables)
345 res += "<table style='margin:0em 0em 0em -0.125em;border-spacing:0px;border:0px;'>";
346
347 // TRANSLATORS: Right ascension/Declination
348 const QString RADec = withDesignations ? QString("α/δ") : qc_("RA/Dec", "celestial coordinate system");
349
350 if (flags&RaDecJ2000)
351 {
352 double dec_j2000, ra_j2000;
353 StelUtils::rectToSphe(&ra_j2000,&dec_j2000,getJ2000EquatorialPos(core));
354 if (withDecimalDegree)
355 {
356 firstCoordinate = StelUtils::radToDecDegStr(ra_j2000,5,false,true);
357 secondCoordinate = StelUtils::radToDecDegStr(dec_j2000);
358 }
359 else
360 {
361 firstCoordinate = StelUtils::radToHmsStr(ra_j2000,true);
362 secondCoordinate = StelUtils::radToDmsStr(dec_j2000,true);
363 }
364
365 if (withTables)
366 res += QString("<tr><td>%1 (J2000.0):</td><td style='text-align:right;'>%2/</td><td style='text-align:right;'>%3</td><td></td></tr>").arg(RADec, firstCoordinate, secondCoordinate);
367 else
368 res += QString("%1 (J2000.0): %2/%3<br/>").arg(RADec, firstCoordinate, secondCoordinate);
369 res += getExtraInfoStrings(RaDecJ2000).join("");
370 res += omgr->getExtraInfoStrings(RaDecJ2000).join("");
371 }
372
373 if (flags&RaDecOfDate)
374 {
375 double dec_equ, ra_equ;
376 StelUtils::rectToSphe(&ra_equ,&dec_equ,getEquinoxEquatorialPos(core));
377 if (withDecimalDegree)
378 {
379 firstCoordinate = StelUtils::radToDecDegStr(ra_equ,5,false,true);
380 secondCoordinate = StelUtils::radToDecDegStr(dec_equ);
381 }
382 else
383 {
384 firstCoordinate = StelUtils::radToHmsStr(ra_equ,true);
385 secondCoordinate = StelUtils::radToDmsStr(dec_equ,true);
386 }
387
388 if (withTables)
389 res += QString("<tr><td>%1 (%4):</td><td style='text-align:right;'>%2/</td><td style='text-align:right;'>%3</td><td></td></tr>").arg(RADec, firstCoordinate, secondCoordinate, cepoch);
390 else
391 res += QString("%1 (%4): %2/%3<br/>").arg(RADec, firstCoordinate, secondCoordinate, cepoch);
392 res += getExtraInfoStrings(RaDecOfDate).join("");
393 res += omgr->getExtraInfoStrings(RaDecOfDate).join("");
394 }
395
396 if (flags&HourAngle)
397 {
398 double dec_sidereal, ra_sidereal, ha_sidereal;
399 StelUtils::rectToSphe(&ra_sidereal,&dec_sidereal,getSiderealPosGeometric(core));
400 ra_sidereal = 2.*M_PI-ra_sidereal;
401 if (withAtmosphere && (alt_app>-2.0*M_PI/180.0)) // Don't show refracted values much below horizon where model is meaningless.
402 {
403 StelUtils::rectToSphe(&ra_sidereal,&dec_sidereal,getSiderealPosApparent(core));
404 ra_sidereal = 2.*M_PI-ra_sidereal;
405 if (withDecimalDegree)
406 {
407 ha_sidereal = ra_sidereal*12/M_PI;
408 if (ha_sidereal>24.)
409 ha_sidereal -= 24.;
410 firstCoordinate = QString("%1h").arg(ha_sidereal, 0, 'f', 5);
411 secondCoordinate = StelUtils::radToDecDegStr(dec_sidereal);
412 }
413 else
414 {
415 firstCoordinate = StelUtils::radToHmsStr(ra_sidereal,true);
416 secondCoordinate = StelUtils::radToDmsStr(dec_sidereal,true);
417 }
418 }
419 else
420 {
421 if (withDecimalDegree)
422 {
423 ha_sidereal = ra_sidereal*12/M_PI;
424 if (ha_sidereal>24.)
425 ha_sidereal -= 24.;
426 firstCoordinate = QString("%1h").arg(ha_sidereal, 0, 'f', 5);
427 secondCoordinate = StelUtils::radToDecDegStr(dec_sidereal);
428 }
429 else
430 {
431 firstCoordinate = StelUtils::radToHmsStr(ra_sidereal,true);
432 secondCoordinate = StelUtils::radToDmsStr(dec_sidereal,true);
433 }
434 }
435
436 // TRANSLATORS: Hour angle/Declination
437 const QString HADec = withDesignations ? QString("h/δ") : qc_("HA/Dec", "celestial coordinate system");
438
439 if (withTables)
440 res += QString("<tr><td>%1:</td><td style='text-align:right;'>%2/</td><td style='text-align:right;'>%3</td><td>%4</td></tr>").arg(HADec, firstCoordinate, secondCoordinate, apparent);
441 else
442 res += QString("%1: %2/%3 %4<br/>").arg(HADec, firstCoordinate, secondCoordinate, apparent);
443 res += getExtraInfoStrings(HourAngle).join("");
444 res += omgr->getExtraInfoStrings(HourAngle).join("");
445 }
446
447 if (flags&AltAzi)
448 {
449 // calculate alt az
450 double az,alt;
451 StelUtils::rectToSphe(&az,&alt,getAltAzPosGeometric(core));
452 double direction = 3.; // N is zero, E is 90 degrees
453 if (useSouthAzimuth)
454 direction = 2.;
455 az = direction*M_PI - az;
456 if (az > M_PI*2)
457 az -= M_PI*2;
458 if (withAtmosphere && (alt_app>-2.0*M_PI/180.0)) // Don't show refracted altitude much below horizon where model is meaningless.
459 {
460 if (withDecimalDegree)
461 {
462 firstCoordinate = StelUtils::radToDecDegStr(az);
463 secondCoordinate = StelUtils::radToDecDegStr(alt_app);
464 }
465 else
466 {
467 firstCoordinate = StelUtils::radToDmsStr(az,true);
468 secondCoordinate = StelUtils::radToDmsStr(alt_app,true);
469 }
470 }
471 else
472 {
473 if (withDecimalDegree)
474 {
475 firstCoordinate = StelUtils::radToDecDegStr(az);
476 secondCoordinate = StelUtils::radToDecDegStr(alt);
477 }
478 else
479 {
480 firstCoordinate = StelUtils::radToDmsStr(az,true);
481 secondCoordinate = StelUtils::radToDmsStr(alt,true);
482 }
483 }
484
485 // TRANSLATORS: Azimuth/Altitude
486 const QString AzAlt = (withDesignations ? "A/a" : qc_("Az./Alt.", "celestial coordinate system"));
487
488 if (withTables)
489 res += QString("<tr><td>%1:</td><td style='text-align:right;'>%2/</td><td style='text-align:right;'>%3</td><td>%4</td></tr>").arg(AzAlt, firstCoordinate, secondCoordinate, apparent);
490 else
491 res += QString("%1: %2/%3 %4<br/>").arg(AzAlt, firstCoordinate, secondCoordinate, apparent);
492 res += getExtraInfoStrings(AltAzi).join("");
493 }
494
495 if (flags&GalacticCoord)
496 {
497 double glong, glat;
498 StelUtils::rectToSphe(&glong, &glat, getGalacticPos(core));
499 if (withDecimalDegree)
500 {
501 firstCoordinate = StelUtils::radToDecDegStr(glong);
502 secondCoordinate = StelUtils::radToDecDegStr(glat);
503 }
504 else
505 {
506 firstCoordinate = StelUtils::radToDmsStr(glong,true);
507 secondCoordinate = StelUtils::radToDmsStr(glat,true);
508 }
509
510 // TRANSLATORS: Galactic longitude/latitude
511 const QString GalLongLat = (withDesignations ? "l/b" : qc_("Gal. long./lat.", "celestial coordinate system"));
512 if (withTables)
513 res += QString("<tr><td>%1:</td><td style='text-align:right;'>%2/</td><td style='text-align:right;'>%3</td><td></td></tr>").arg(GalLongLat, firstCoordinate, secondCoordinate);
514 else
515 res += QString("%1: %2/%3<br/>").arg(GalLongLat, firstCoordinate, secondCoordinate);
516 res += getExtraInfoStrings(GalacticCoord).join("");
517 }
518
519 if (flags&SupergalacticCoord)
520 {
521 double sglong, sglat;
522 StelUtils::rectToSphe(&sglong, &sglat, getSupergalacticPos(core));
523 if (withDecimalDegree)
524 {
525 firstCoordinate = StelUtils::radToDecDegStr(sglong);
526 secondCoordinate = StelUtils::radToDecDegStr(sglat);
527 }
528 else
529 {
530 firstCoordinate = StelUtils::radToDmsStr(sglong,true);
531 secondCoordinate = StelUtils::radToDmsStr(sglat,true);
532 }
533
534 // TRANSLATORS: Supergalactic longitude/latitude
535 const QString SGalLongLat = (withDesignations ? "SGL/SGB" : qc_("Supergal. long./lat.", "celestial coordinate system"));
536
537 if (withTables)
538 res += QString("<tr><td>%1:</td><td style='text-align:right;'>%2/</td><td style='text-align:right;'>%3</td><td></td></tr>").arg(SGalLongLat, firstCoordinate, secondCoordinate);
539 else
540 res += QString("%1: %2/%3<br/>").arg(SGalLongLat, firstCoordinate, secondCoordinate);
541 res += getExtraInfoStrings(SupergalacticCoord).join("");
542 }
543
544 // N.B. Ecliptical coordinates are particularly earth-bound.
545 // It may be OK to have terrestrial ecliptical coordinates of J2000.0 (standard epoch) because those are in practice linked with VSOP XY plane,
546 // and because the ecliptical grid of J2000 is also shown for observers on other planets.
547 // The formulation here has never computed the true position of any observer planet's orbital plane except for Earth,
548 // or ever displayed the coordinates in the observer planet's equivalent to Earth's ecliptical coordinates.
549 // As quick test you can observe if in any "Ecliptic coordinate" as seen from e.g. Mars or Jupiter the Sun was ever close to beta=0 (except if crossing the node...).
550
551 // TRANSLATORS: Ecliptic longitude/latitude
552 const QString EqlLongLat = (withDesignations ? QString("λ/β") :
553 qc_("Ecl. long./lat.", "celestial coordinate system") );
554
555 if (flags&EclipticCoordJ2000)
556 {
557 const double eclJ2000=GETSTELMODULE(SolarSystem)->getEarth()->getRotObliquity(2451545.0);
558 double ra_equ, dec_equ, lambda, beta;
559 StelUtils::rectToSphe(&ra_equ,&dec_equ,getJ2000EquatorialPos(core));
560 StelUtils::equToEcl(ra_equ, dec_equ, eclJ2000, &lambda, &beta);
561 if (lambda<0) lambda+=2.0*M_PI;
562 if (withDecimalDegree)
563 {
564 firstCoordinate = StelUtils::radToDecDegStr(lambda);
565 secondCoordinate = StelUtils::radToDecDegStr(beta);
566 }
567 else
568 {
569 firstCoordinate = StelUtils::radToDmsStr(lambda, true);
570 secondCoordinate = StelUtils::radToDmsStr(beta, true);
571 }
572
573 if (withTables)
574 res += QString("<tr><td>%1 (J2000.0):</td><td style='text-align:right;'>%2/</td><td style='text-align:right;'>%3</td><td></td></tr>").arg(EqlLongLat, firstCoordinate, secondCoordinate);
575 else
576 res += QString("%1 (J2000.0): %2/%3<br/>").arg(EqlLongLat, firstCoordinate, secondCoordinate);
577 res += getExtraInfoStrings(EclipticCoordJ2000).join("");
578 }
579
580 if ((flags&EclipticCoordOfDate) && (QString("Earth Sun").contains(currentPlanet)))
581 {
582 const double jde=core->getJDE();
583 double eclJDE = GETSTELMODULE(SolarSystem)->getEarth()->getRotObliquity(jde);
584 if (StelApp::getInstance().getCore()->getUseNutation())
585 {
586 double deltaEps, deltaPsi;
587 getNutationAngles(jde, &deltaPsi, &deltaEps);
588 eclJDE+=deltaEps;
589 }
590 double ra_equ, dec_equ, lambdaJDE, betaJDE;
591
592 StelUtils::rectToSphe(&ra_equ,&dec_equ,getEquinoxEquatorialPos(core));
593 StelUtils::equToEcl(ra_equ, dec_equ, eclJDE, &lambdaJDE, &betaJDE);
594 if (lambdaJDE<0) lambdaJDE+=2.0*M_PI;
595 if (withDecimalDegree)
596 {
597 firstCoordinate = StelUtils::radToDecDegStr(lambdaJDE);
598 secondCoordinate = StelUtils::radToDecDegStr(betaJDE);
599 }
600 else
601 {
602 firstCoordinate = StelUtils::radToDmsStr(lambdaJDE, true);
603 secondCoordinate = StelUtils::radToDmsStr(betaJDE, true);
604 }
605
606 if (withTables)
607 res += QString("<tr><td>%1 (%4):</td><td style='text-align:right;'>%2/</td><td style='text-align:right;'>%3</td><td></td></tr>").arg(EqlLongLat, firstCoordinate, secondCoordinate, cepoch) + "</table>";
608 else
609 res += QString("%1 (%4): %2/%3<br/>").arg(EqlLongLat, firstCoordinate, secondCoordinate, cepoch);
610 res += getExtraInfoStrings(EclipticCoordOfDate).join("");
611
612 // GZ Only for now: display epsilon_A, angle between Earth's Axis and ecl. of date.
613 if (withDecimalDegree)
614 firstCoordinate = StelUtils::radToDecDegStr(eclJDE);
615 else
616 firstCoordinate = StelUtils::radToDmsStr(eclJDE, true);
617
618 QString eqlObl = q_("Ecliptic obliquity");
619 if (withTables)
620 {
621 res += "<table style='margin:0em 0em 0em -0.125em;border-spacing:0px;border:0px;'>";
622 res += QString("<tr><td>%1 (%4):</td><td>%2</td></tr>").arg(eqlObl, firstCoordinate, cepoch);
623 }
624 else
625 res += QString("%1 (%3): %2<br/>").arg(eqlObl, firstCoordinate, cepoch);
626 }
627
628 if (withTables)
629 res += "</table>";
630
631 // Specialized plugins (e.g. Astro Navigation or ethno-astronomical specialties) may want to provide additional types of coordinates here.
632 if (flags&OtherCoord)
633 {
634 if (withTables)
635 res += "<table style='margin:0em 0em 0em -0.125em;border-spacing:0px;border:0px;'>";
636 res += getExtraInfoStrings(OtherCoord).join("");
637 res += omgr->getExtraInfoStrings(OtherCoord).join("");
638 if (withTables)
639 res += "</table>";
640 }
641
642 if ((flags&SiderealTime) && (currentPlanet==QStringLiteral("Earth")))
643 {
644 const double longitude=static_cast<double>(core->getCurrentLocation().longitude);
645 double sidereal=(get_mean_sidereal_time(core->getJD(), core->getJDE()) + longitude) / 15.;
646 sidereal=StelUtils::fmodpos(sidereal, 24.);
647 QString STc = q_("Mean Sidereal Time");
648 QString STd = StelUtils::hoursToHmsStr(sidereal);
649
650 if (withTables)
651 {
652 res += "<table style='margin:0em 0em 0em -0.125em;border-spacing:0px;border:0px;'>";
653 res += QString("<tr><td>%1:</td><td style='text-align:right;'>%2</td></tr>").arg(STc, STd);
654 }
655 else
656 res += QString("%1: %2<br/>").arg(STc, STd);
657
658 if (core->getUseNutation())
659 {
660 sidereal=(get_apparent_sidereal_time(core->getJD(), core->getJDE()) + longitude) / 15.;
661 sidereal=StelUtils::fmodpos(sidereal, 24.);
662 STc = q_("Apparent Sidereal Time");
663 STd = StelUtils::hoursToHmsStr(sidereal);
664 if (withTables)
665 res += QString("<tr><td>%1:</td><td style='text-align:right;'>%2</td></tr>").arg(STc, STd);
666 else
667 res += QString("%1: %2<br/>").arg(STc, STd);
668 }
669 res += getExtraInfoStrings(flags&SiderealTime).join("");
670 res += omgr->getExtraInfoStrings(flags&SiderealTime).join("");
671 if (withTables && !(flags&RTSTime && getType()!=QStringLiteral("Satellite")))
672 res += "</table>";
673 }
674
675 if (flags&RTSTime && getType()!=QStringLiteral("Satellite") && !currentPlanet.contains("observer", Qt::CaseInsensitive) && !(core->getCurrentLocation().name.contains("->")))
676 {
677 const double utcShift = core->getUTCOffset(core->getJD()) / 24.; // Fix DST shift...
678 Vec4d rts = getRTSTime(core);
679 QString sTransit = qc_("Transit", "celestial event; passage across a meridian");
680 QString sRise = qc_("Rise", "celestial event");
681 QString sSet = qc_("Set", "celestial event");
682 double sunrise = 0.;
683 double sunset = 24.;
684 const bool isSun = (getEnglishName()=="Sun");
685 double hour(0);
686
687 if (withTables && !(flags&SiderealTime && currentPlanet==QStringLiteral("Earth")))
688 res += "<table style='margin:0em 0em 0em -0.125em;border-spacing:0px;border:0px;'>";
689
690 if (rts[3]==0.)
691 {
692 hour = StelUtils::getHoursFromJulianDay(rts[0]+utcShift);
693 if (withTables)
694 res += QString("<tr><td>%1:</td><td style='text-align:right;'>%2</td></tr>").arg(sRise, StelUtils::hoursToHmsStr(hour, true));
695 else
696 res += QString("%1: %2<br/>").arg(sRise, StelUtils::hoursToHmsStr(hour, true));
697
698 sunrise = hour;
699 }
700
701 hour = StelUtils::getHoursFromJulianDay(rts[1]+utcShift);
702 if (withTables)
703 res += QString("<tr><td>%1:</td><td style='text-align:right;'>%2</td></tr>").arg(sTransit, StelUtils::hoursToHmsStr(hour, true));
704 else
705 res += QString("%1: %2<br/>").arg(sTransit, StelUtils::hoursToHmsStr(hour, true));
706
707 if (rts[3]==0.)
708 {
709 hour = StelUtils::getHoursFromJulianDay(rts[2]+utcShift);
710 if (withTables)
711 res += QString("<tr><td>%1:</td><td style='text-align:right;'>%2</td></tr>").arg(sSet, StelUtils::hoursToHmsStr(hour, true));
712 else
713 res += QString("%1: %2<br/>").arg(sSet, StelUtils::hoursToHmsStr(hour, true));
714
715 sunset = hour;
716 }
717
718 if (isSun)
719 {
720 QString sMTwilight = qc_("Morning twilight", "celestial event");
721 QString sETwilight = qc_("Evening twilight", "celestial event");
722 const double twilightAltitude = omgr->getTwilightAltitude();
723 QString alt = QString::number(twilightAltitude, 'f', 1);
724 Vec4d twilight = getRTSTime(core, twilightAltitude);
725 if (twilight[3]==0.)
726 {
727 hour = StelUtils::getHoursFromJulianDay(twilight[0]+utcShift);
728 if (withTables)
729 res += QString("<tr><td>%1 (h=%2°):</td><td style='text-align:right;'>%3</td></tr>").arg(sMTwilight, alt, StelUtils::hoursToHmsStr(hour, true));
730 else
731 res += QString("%1 (h=%2°): %3<br/>").arg(sMTwilight, alt, StelUtils::hoursToHmsStr(hour, true));
732
733 hour = StelUtils::getHoursFromJulianDay(twilight[2]+utcShift);
734 if (withTables)
735 res += QString("<tr><td>%1 (h=%2°):</td><td style='text-align:right;'>%3</td></tr>").arg(sETwilight, alt, StelUtils::hoursToHmsStr(hour, true));
736 else
737 res += QString("%1 (h=%2°): %3<br/>").arg(sETwilight, alt, StelUtils::hoursToHmsStr(hour, true));
738 }
739 double lengthOfDay = sunset - sunrise;
740 if (lengthOfDay<24.)
741 {
742 QString sDay = q_("Daytime");
743 if (withTables)
744 res += QString("<tr><td>%1:</td><td style='text-align:right;'>%2</td></tr>").arg(sDay, StelUtils::hoursToHmsStr(lengthOfDay, true));
745 else
746 res += QString("%1: %2<br/>").arg(sDay, StelUtils::hoursToHmsStr(lengthOfDay, true));
747 }
748 }
749
750 if (withTables)
751 res += "</table>";
752
753 if (rts[3]<0.)
754 {
755 if (isSun)
756 res += q_("Polar night") + "<br />";
757 else
758 res += q_("This object never rises") + "<br />";
759 }
760 else if (rts[3]>0.)
761 {
762 if (isSun)
763 res += q_("Polar day") + "<br />";
764 else
765 res += q_("Circumpolar (never sets)") + "<br />";
766 }
767 // These never could have been seen before (??)
768 //else if (rts[0]>99. && rts[2]<99.)
769 // res += q_("Polar dawn") + "<br />";
770 //else if (rts[0]<99. && rts[2]>99.)
771 // res += q_("Polar dusk") + "<br />";
772
773
774 // Greatest Digression: limiting azimuth and hour angles for stars with upper culmination between pole and zenith
775 double dec_equ, ra_equ;
776 StelUtils::rectToSphe(&ra_equ,&dec_equ,getEquinoxEquatorialPos(core));
777 const double latitude=static_cast<double>(core->getCurrentLocation().latitude)*M_PI_180;
778 if (((latitude>0.) && (dec_equ>=latitude)) || ((latitude<0.) && (dec_equ<=latitude)))
779 {
780 const double theta=acos(tan(latitude)/tan(dec_equ)); // hour angle
781 double az=asin(cos(dec_equ)/cos(latitude)); // azimuth (eastern)
782 // TRANSLATORS: Greatest Eastern Digression is the maximum azimuth for stars with upper culmination between pole and zenith
783 QString event(q_("Max. E. Digression"));
784 // TRANSLATORS: azimuth (abbrev.)
785 QString azStr=(withDesignations? qc_("A", "celestial coordinate system") : qc_("Az.", "celestial coordinate system"));
786 // Translators: hour angle (abbrev.)
787 QString haStr=(withDesignations? qc_("h", "celestial coordinate system") : qc_("HA", "celestial coordinate system"));
788 if (latitude<0.)
789 az=M_PI-az;
790 if (StelApp::getInstance().getFlagSouthAzimuthUsage())
791 az+=M_PI;
792
793 if (withDecimalDegree)
794 {
795 firstCoordinate = StelUtils::radToDecDegStr(az,5,false,true);
796 secondCoordinate = StelUtils::radToDecDegStr(-theta);
797 }
798 else
799 {
800 firstCoordinate = StelUtils::radToDmsStr(az,true);
801 secondCoordinate = StelUtils::radToHmsStr(-theta,true);
802 }
803
804 if (withTables)
805 {
806 res += "<table style='margin:0em 0em 0em -0.125em;border-spacing:0px;border:0px;'>";
807 res += QString("<tr><td>%1:</td><td style='text-align:right;'>%2:</td><td style='text-align:right;'>%3</td><td style='text-align:right;'>/%4:</td><td style='text-align:right;'>%5</td></tr>").arg(event, azStr, firstCoordinate, haStr, secondCoordinate);
808 }
809 else
810 res += QString("%1: %2=%3, %4=%5<br/>").arg(event, azStr, firstCoordinate, haStr, secondCoordinate);
811
812 // TRANSLATORS: Greatest Western Digression is the maximum western azimuth for stars with upper culmination between pole and zenith
813 event=q_("Max. W. Digression");
814 if (withDecimalDegree)
815 {
816 firstCoordinate = StelUtils::radToDecDegStr(StelUtils::fmodpos(-az, 2.*M_PI),5,false,true);
817 secondCoordinate = StelUtils::radToDecDegStr(theta);
818 }
819 else
820 {
821 firstCoordinate = StelUtils::radToDmsStr(StelUtils::fmodpos(-az, 2.*M_PI),true);
822 secondCoordinate = StelUtils::radToHmsStr(theta,true);
823 }
824
825 if (withTables)
826 {
827 res += QString("<tr><td>%1:</td><td style='text-align:right;'>%2:</td><td style='text-align:right;'>%3</td><td style='text-align:right;'>/%4:</td><td style='text-align:right;'>%5</td></tr>").arg(event, azStr, firstCoordinate, haStr, secondCoordinate);
828 res += QString("</table>");
829 }
830 else
831 res += QString("%1: %2=%3, %4=%5<br/>").arg(event, azStr, firstCoordinate, haStr, secondCoordinate);
832 }
833 res += getExtraInfoStrings(flags&RTSTime).join(' ');
834 res += omgr->getExtraInfoStrings(flags&RTSTime).join(' ');
835 }
836
837 if (flags&Extra)
838 {
839 if (getType()!=QStringLiteral("Star"))
840 {
841 QString pa;
842 const double par = static_cast<double>(getParallacticAngle(core));
843 if (withDecimalDegree)
844 pa = StelUtils::radToDecDegStr(par);
845 else
846 pa = StelUtils::radToDmsStr(par, true);
847
848 res += QString("%1: %2<br/>").arg(q_("Parallactic Angle")).arg(pa);
849 }
850 res += getExtraInfoStrings(Extra).join("");
851 res += omgr->getExtraInfoStrings(Extra).join("");
852 }
853
854 if (flags&IAUConstellation)
855 {
856 QString constel=core->getIAUConstellation(getEquinoxEquatorialPos(core));
857 res += QString("%1: %2<br/>").arg(q_("IAU Constellation"), constel);
858 res += getExtraInfoStrings(flags&IAUConstellation).join("");
859 res += omgr->getExtraInfoStrings(flags&IAUConstellation).join("");
860 }
861
862 return res;
863 }
864
865 // Apply post processing on the info string
postProcessInfoString(QString & str,const InfoStringGroup & flags) const866 void StelObject::postProcessInfoString(QString& str, const InfoStringGroup& flags) const
867 {
868 StelObjectMgr* omgr;
869 omgr=GETSTELMODULE(StelObjectMgr);
870 str.append(getExtraInfoStrings(Script).join(' '));
871 str.append(omgr->getExtraInfoStrings(Script).join(' '));
872 str.append(getExtraInfoStrings(DebugAid).join(' ')); // TBD: Remove for Release builds?
873 str.append(omgr->getExtraInfoStrings(DebugAid).join(' ')); // TBD: Remove for Release builds?
874
875 // hack for avoiding an empty line before table
876 str.replace(QRegularExpression("<br(\\s*/)?><table"), "<table");
877 // chomp trailing line breaks
878 str.replace(QRegularExpression("<br(\\s*/)?>\\s*$"), "");
879
880 if (flags&PlainText)
881 {
882 str.replace("<b>", "");
883 str.replace("</b>", "");
884 str.replace("<h2>", "");
885 str.replace("</h2>", "\n");
886 str.replace(QRegularExpression("<br(\\s*/)?>"), "\n");
887 str.replace("<tr>", "");
888 str.replace(QRegularExpression("<td(\\w*)?>"), "");
889 str.replace("<td>", "");
890 str.replace("</tr>", "\n");
891 str.replace(QRegularExpression("<table(\\w*)?>"), "");
892 str.replace("</table>", "");
893 }
894 else if(!(flags&NoFont))
895 {
896 Vec3f color = getInfoColor();
897 StelCore* core = StelApp::getInstance().getCore();
898 if (StelApp::getInstance().getFlagOverwriteInfoColor())
899 {
900 // make info text more readable...
901 color = StelApp::getInstance().getOverwriteInfoColor();
902 }
903 if (core->isBrightDaylight() && !StelApp::getInstance().getVisionModeNight())
904 {
905 // make info text more readable when atmosphere enabled at daylight.
906 color = StelApp::getInstance().getDaylightInfoColor();
907 }
908 str.prepend(QString("<font color=%1>").arg(color.toHtmlColor()));
909 str.append(QString("</font>"));
910 }
911 }
912
getInfoMap(const StelCore * core) const913 QVariantMap StelObject::getInfoMap(const StelCore *core) const
914 {
915 QVariantMap map;
916
917 Vec3d pos;
918 double ra, dec, alt, az, glong, glat;
919 bool useOldAzimuth = StelApp::getInstance().getFlagSouthAzimuthUsage();
920
921 map.insert("type", getType());
922 // ra/dec
923 pos = getEquinoxEquatorialPos(core);
924 StelUtils::rectToSphe(&ra, &dec, pos);
925 map.insert("ra", ra*M_180_PI);
926 map.insert("dec", dec*M_180_PI);
927 map.insert("iauConstellation", core->getIAUConstellation(pos));
928
929 if (getType()!=QStringLiteral("Star"))
930 map.insert("parallacticAngle", static_cast<double>(getParallacticAngle(core))*M_180_PI);
931
932 // Sidereal Time and hour angle
933 if (core->getCurrentLocation().planetName=="Earth")
934 {
935 const double longitude=static_cast<double>(core->getCurrentLocation().longitude);
936 double sidereal=(get_mean_sidereal_time(core->getJD(), core->getJDE()) + longitude) / 15.;
937 sidereal=fmod(sidereal, 24.);
938 if (sidereal < 0.) sidereal+=24.;
939 map.insert("meanSidTm", StelUtils::hoursToHmsStr(sidereal));
940
941 sidereal=(get_apparent_sidereal_time(core->getJD(), core->getJDE()) + longitude) / 15.;
942 sidereal=fmod(sidereal, 24.);
943 if (sidereal < 0.) sidereal+=24.;
944 map.insert("appSidTm", StelUtils::hoursToHmsStr(sidereal));
945
946 double ha = sidereal * 15.0 - ra * M_180_PI;
947 ha=fmod(ha, 360.0);
948 if (ha < 0.) ha+=360.0;
949 map.insert("hourAngle-dd", ha);
950 map.insert("hourAngle-hms", StelUtils::hoursToHmsStr(ha/15.0));
951 }
952
953 // ra/dec in J2000
954 pos = getJ2000EquatorialPos(core);
955 StelUtils::rectToSphe(&ra, &dec, pos);
956 map.insert("raJ2000", ra*M_180_PI);
957 map.insert("decJ2000", dec*M_180_PI);
958
959 // apparent altitude/azimuth
960 pos = getAltAzPosApparent(core);
961 StelUtils::rectToSphe(&az, &alt, pos);
962 double direction = 3.; // N is zero, E is 90 degrees
963 if (useOldAzimuth)
964 direction = 2.;
965 az = direction*M_PI - az;
966 if (az > M_PI*2)
967 az -= M_PI*2;
968
969 map.insert("altitude", alt*M_180_PI);
970 map.insert("azimuth", az*M_180_PI);
971 map.insert("airmass", getAirmass(core));
972
973 // geometric altitude/azimuth
974 pos = getAltAzPosGeometric(core);
975 StelUtils::rectToSphe(&az, &alt, pos);
976 az = direction*M_PI - az;
977 if (az > M_PI*2)
978 az -= M_PI*2;
979
980 map.insert("altitude-geometric", alt*M_180_PI);
981 map.insert("azimuth-geometric", az*M_180_PI);
982
983 // galactic long/lat
984 pos = getGalacticPos(core);
985 StelUtils::rectToSphe(&glong, &glat, pos);
986 map.insert("glong", glong*M_180_PI);
987 map.insert("glat", glat*M_180_PI);
988
989 // supergalactic long/lat
990 pos = getSupergalacticPos(core);
991 StelUtils::rectToSphe(&glong, &glat, pos);
992 map.insert("sglong", glong*M_180_PI);
993 map.insert("sglat", glat*M_180_PI);
994
995 SolarSystem* ssmgr = GETSTELMODULE(SolarSystem);
996 double ra_equ, dec_equ, lambda, beta;
997 // J2000
998 double eclJ2000 = ssmgr->getEarth()->getRotObliquity(2451545.0);
999 double ecl = ssmgr->getEarth()->getRotObliquity(core->getJDE());
1000
1001 // ecliptic longitude/latitude (J2000 frame)
1002 StelUtils::rectToSphe(&ra_equ,&dec_equ, getJ2000EquatorialPos(core));
1003 StelUtils::equToEcl(ra_equ, dec_equ, eclJ2000, &lambda, &beta);
1004 if (lambda<0) lambda+=2.0*M_PI;
1005 map.insert("elongJ2000", lambda*M_180_PI);
1006 map.insert("elatJ2000", beta*M_180_PI);
1007
1008 if (QString("Earth Sun").contains(core->getCurrentLocation().planetName))
1009 {
1010 // ecliptic longitude/latitude
1011 StelUtils::rectToSphe(&ra_equ,&dec_equ, getEquinoxEquatorialPos(core));
1012 StelUtils::equToEcl(ra_equ, dec_equ, ecl, &lambda, &beta);
1013 if (lambda<0) lambda+=2.0*M_PI;
1014 map.insert("elong", lambda*M_180_PI);
1015 map.insert("elat", beta*M_180_PI);
1016 }
1017
1018 // magnitude
1019 map.insert("vmag", getVMagnitude(core));
1020 map.insert("vmage", getVMagnitudeWithExtinction(core));
1021
1022 // angular size
1023 double angularSize = 2.*getAngularSize(core)*M_PI_180;
1024 bool sign;
1025 double deg;
1026 StelUtils::radToDecDeg(angularSize, sign, deg);
1027 if (!sign)
1028 deg *= -1;
1029 map.insert("size", angularSize);
1030 map.insert("size-dd", deg);
1031 map.insert("size-deg", StelUtils::radToDecDegStr(angularSize, 5));
1032 map.insert("size-dms", StelUtils::radToDmsPStr(angularSize, 2));
1033
1034 // english name or designation & localized name
1035 map.insert("name", getEnglishName());
1036 map.insert("localized-name", getNameI18n());
1037
1038 // 'above horizon' flag
1039 map.insert("above-horizon", isAboveRealHorizon(core));
1040
1041 Vec4d rts = getRTSTime(core);
1042 if (rts[3]>-1000.)
1043 {
1044 const double utcShift = core->getUTCOffset(core->getJD()) / 24.; // Fix DST shift...
1045 int hr, min, sec;
1046 StelUtils::getTimeFromJulianDay(rts[1]+utcShift, &hr, &min, &sec);
1047 double hours=hr+static_cast<double>(min)/60. + static_cast<double>(sec)/3600.;
1048
1049 map.insert("transit", StelUtils::hoursToHmsStr(hours, true));
1050 map.insert("transit-dhr", hours);
1051 if (rts[3]==0.)
1052 {
1053 StelUtils::getTimeFromJulianDay(rts[0]+utcShift, &hr, &min, &sec);
1054 hours=hr+static_cast<double>(min)/60. + static_cast<double>(sec)/3600.;
1055 map.insert("rise", StelUtils::hoursToHmsStr(hours, true));
1056 map.insert("rise-dhr", hours);
1057 StelUtils::getTimeFromJulianDay(rts[2]+utcShift, &hr, &min, &sec);
1058 hours=hr+static_cast<double>(min)/60. + static_cast<double>(sec)/3600.;
1059 map.insert("set", StelUtils::hoursToHmsStr(hours, true));
1060 map.insert("set-dhr", hours);
1061 }
1062 else {
1063 map.insert("rise", "---");
1064 map.insert("set", "---");
1065 }
1066 }
1067 return map;
1068 }
1069
setExtraInfoString(const InfoStringGroup & flags,const QString & str)1070 void StelObject::setExtraInfoString(const InfoStringGroup& flags, const QString &str)
1071 {
1072 extraInfoStrings.remove(flags); // delete all entries with these flags
1073 if (str.length()>0)
1074 extraInfoStrings.insert(flags, str);
1075 }
addToExtraInfoString(const StelObject::InfoStringGroup & flags,const QString & str)1076 void StelObject::addToExtraInfoString(const StelObject::InfoStringGroup &flags, const QString &str)
1077 {
1078 // Avoid insertion of full duplicates!
1079 if (!extraInfoStrings.contains(flags, str))
1080 extraInfoStrings.insertMulti(flags, str);
1081 }
1082
getExtraInfoStrings(const InfoStringGroup & flags) const1083 QStringList StelObject::getExtraInfoStrings(const InfoStringGroup& flags) const
1084 {
1085 QStringList list;
1086 QMultiMap<InfoStringGroup, QString>::const_iterator i = extraInfoStrings.constBegin();
1087 while (i != extraInfoStrings.constEnd())
1088 {
1089 if (i.key() & flags)
1090 {
1091 QString val=i.value();
1092 if (flags&DebugAid)
1093 val.prepend("DEBUG: ");
1094 // For unclear reasons the sequence of entries can be preserved by *pre*pending in the returned list.
1095 list.prepend(val);
1096 }
1097 ++i;
1098 }
1099 return list;
1100 }
1101
removeExtraInfoStrings(const InfoStringGroup & flags)1102 void StelObject::removeExtraInfoStrings(const InfoStringGroup& flags)
1103 {
1104 QMultiMap<InfoStringGroup, QString>::iterator i = extraInfoStrings.begin();
1105 while (i != extraInfoStrings.end())
1106 {
1107 if (i.key() & flags)
1108 i=extraInfoStrings.erase(i);
1109 else
1110 ++i;
1111 }
1112 }
1113
1114 // Add horizontal coordinates of Sun and Moon where useful
getSolarLunarInfoString(const StelCore * core,const InfoStringGroup & flags) const1115 QString StelObject::getSolarLunarInfoString(const StelCore *core, const InfoStringGroup& flags) const
1116 {
1117 QString str;
1118 QTextStream oss(&str);
1119 static SolarSystem *ssystem=GETSTELMODULE(SolarSystem);
1120 PlanetP earth = ssystem->getEarth();
1121 if ((core->getCurrentPlanet()==earth) && (flags&SolarLunarPosition))
1122 {
1123 const bool withTables = StelApp::getInstance().getFlagUseFormattingOutput();
1124 const bool withDecimalDegree = StelApp::getInstance().getFlagShowDecimalDegrees();
1125
1126 if (withTables)
1127 oss << "<table style='margin:0em 0em 0em -0.125em;border-spacing:0px;border:0px;'>";
1128 const bool useSouthAzimuth = StelApp::getInstance().getFlagSouthAzimuthUsage();
1129 const bool withDesignations = StelApp::getInstance().getFlagUseCCSDesignation();
1130 double az, alt;
1131 QString azStr, altStr;
1132
1133 if (getEnglishName()!="Sun")
1134 {
1135 StelUtils::rectToSphe(&az,&alt,ssystem->getSun()->getAltAzPosAuto(core));
1136 az = (useSouthAzimuth? 2. : 3.)*M_PI - az;
1137 if (az > M_PI*2)
1138 az -= M_PI*2;
1139 azStr = (withDecimalDegree ? StelUtils::radToDecDegStr(az, 2) : StelUtils::radToDmsStr(az,false));
1140 altStr = (withDecimalDegree ? StelUtils::radToDecDegStr(alt, 2) : StelUtils::radToDmsStr(alt,false));
1141
1142 // TRANSLATORS: Azimuth/Altitude
1143 const QString SolarAzAlt = (withDesignations ? qc_("Solar A/a", "celestial coordinate system") : qc_("Solar Az./Alt.", "celestial coordinate system"));
1144 if (withTables)
1145 {
1146 oss << QString("<tr><td>%1:</td><td style='text-align:right;'>%2/</td><td style='text-align:right;'>%3</td></tr>").arg(SolarAzAlt, azStr, altStr);
1147 }
1148 else
1149 oss << QString("%1: %2/%3<br/>").arg(SolarAzAlt, azStr, altStr);
1150 }
1151 if (getEnglishName()!="Moon")
1152 {
1153 StelUtils::rectToSphe(&az,&alt,ssystem->getMoon()->getAltAzPosAuto(core));
1154 az = (useSouthAzimuth? 2. : 3.)*M_PI - az;
1155 if (az > M_PI*2)
1156 az -= M_PI*2;
1157 azStr = (withDecimalDegree ? StelUtils::radToDecDegStr(az, 2) : StelUtils::radToDmsStr(az,false));
1158 altStr = (withDecimalDegree ? StelUtils::radToDecDegStr(alt, 2) : StelUtils::radToDmsStr(alt,false));
1159
1160 // TRANSLATORS: Azimuth/Altitude
1161 const QString LunarAzAlt = (withDesignations ? qc_("Lunar A/a", "celestial coordinate system") : qc_("Lunar Az./Alt.", "celestial coordinate system"));
1162 if (withTables)
1163 {
1164 oss << QString("<tr><td>%1:</td><td style='text-align:right;'>%2/</td><td style='text-align:right;'>%3</td></tr>").arg(LunarAzAlt, azStr, altStr);
1165 }
1166 else
1167 oss << QString("%1: %2/%3<br/>").arg(LunarAzAlt, azStr, altStr);
1168 }
1169 if (withTables)
1170 oss << "</table>";
1171 }
1172 return str;
1173 }
1174