Kstars

dms.cpp
1/*
2 SPDX-FileCopyrightText: 2001 Jason Harris <jharris@30doradus.org>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "dms.h"
8
9#include <QLocale>
10
11#include <QRegExp>
12
13// Qt version calming
14#include <qtskipemptyparts.h>
15
16#ifdef COUNT_DMS_SINCOS_CALLS
17long unsigned dms::dms_constructor_calls = 0;
18long unsigned dms::dms_with_sincos_called = 0;
19long unsigned dms::trig_function_calls = 0;
20long unsigned dms::redundant_trig_function_calls = 0;
21double dms::seconds_in_trig = 0;
22#endif
23
24void dms::setD(const int &d, const int &m, const int &s, const int &ms)
25{
26 D = (double)abs(d) + ((double)m + ((double)s + (double)ms / 1000.) / 60.) / 60.;
27 if (d < 0)
28 {
29 D = -1.0 * D;
30 }
31#ifdef COUNT_DMS_SINCOS_CALLS
32 m_cosDirty = m_sinDirty = true;
33#endif
34}
35
36void dms::setH(const int &h, const int &m, const int &s, const int &ms)
37{
38 D = 15.0 * ((double)abs(h) + ((double)m + ((double)s + (double)ms / 1000.) / 60.) / 60.);
39 if (h < 0)
40 {
41 D = -1.0 * D;
42 }
43#ifdef COUNT_DMS_SINCOS_CALLS
44 m_cosDirty = m_sinDirty = true;
45#endif
46}
47
48bool dms::setFromString(const QString &str, bool isDeg)
49{
50 int d(0), m(0);
51 double s(0.0);
52 bool checkValue(false), badEntry(false), negative(false);
53 QString entry = str.trimmed();
54 entry.remove(QRegExp("[hdms'\"°]"));
55
56 //Account for localized decimal-point settings
57 //QString::toDouble() requires that the decimal symbol is "."
58 entry.replace(QLocale().decimalPoint(), ".");
59
60 //empty entry returns false
61 if (entry.isEmpty())
62 {
63 dms::setD(NaN::d);
64 return false;
65 }
66
67 //try parsing a simple integer
68 d = entry.toInt(&checkValue);
69 if (checkValue)
70 {
71 if (isDeg)
72 dms::setD(d, 0, 0);
73 else
74 dms::setH(d, 0, 0);
75 return true;
76 }
77
78 //try parsing a simple double
79 double x = entry.toDouble(&checkValue);
80 if (checkValue)
81 {
82 if (isDeg)
83 dms::setD(x);
84 else
85 dms::setH(x);
86 return true;
87 }
88
89 //try parsing multiple fields.
90 QStringList fields;
91
92 //check for colon-delimiters or space-delimiters
93 if (entry.contains(':'))
94 fields = entry.split(':', Qt::SkipEmptyParts);
95 else
96 fields = entry.split(' ', Qt::SkipEmptyParts);
97
98 //anything with one field is invalid!
99 if (fields.count() == 1)
100 {
101 dms::setD(NaN::d);
102 return false;
103 }
104
105 //If two fields we will add a third one, and then parse with
106 //the 3-field code block. If field[1] is an int, add a third field equal to "0".
107 //If field[1] is a double, convert it to integer arcmin, and convert
108 //the remainder to integer arcsec
109 //If field[1] is neither int nor double, return false.
110 if (fields.count() == 2)
111 {
112 m = fields[1].toInt(&checkValue);
113 if (checkValue)
114 fields.append(QString("0"));
115 else
116 {
117 double mx = fields[1].toDouble(&checkValue);
118 if (checkValue)
119 {
120 fields[1] = QString::number(int(mx));
121 fields.append(QString::number(int(60.0 * (mx - int(mx)))));
122 }
123 else
124 {
125 dms::setD(NaN::d);
126 return false;
127 }
128 }
129 }
130
131 //Now have (at least) three fields ( h/d m s );
132 //we can ignore anything after 3rd field
133 if (fields.count() >= 3)
134 {
135 //See if first two fields parse as integers, and third field as a double
136
137 d = fields[0].toInt(&checkValue);
138 if (!checkValue)
139 badEntry = true;
140 m = fields[1].toInt(&checkValue);
141 if (!checkValue)
142 badEntry = true;
143 s = fields[2].toDouble(&checkValue);
144 if (!checkValue)
145 badEntry = true;
146
147 //Special case: If first field is "-0", store the negative sign.
148 //(otherwise it gets dropped)
149 if (fields[0].at(0) == '-' && d == 0)
150 negative = true;
151 }
152
153 if (!badEntry)
154 {
155 double D = (double)abs(d) + (double)abs(m) / 60. + (double)fabs(s) / 3600.;
156
157 if (negative || d < 0 || m < 0 || s < 0)
158 {
159 D = -1.0 * D;
160 }
161
162 if (isDeg)
163 {
164 dms::setD(D);
165 }
166 else
167 {
168 dms::setH(D);
169 }
170 }
171 else
172 {
173 dms::setD(NaN::d);
174 return false;
175 }
176
177 return true;
178}
179
180int dms::arcmin(void) const
181{
182 if (std::isnan(D))
183 return 0;
184
185 int am = int(60.0 * (fabs(D) - abs(degree())));
186 if (D < 0.0 && D > -1.0) //angle less than zero, but greater than -1.0
187 {
188 am = -1 * am; //make minute negative
189 }
190 return am; // Warning: Will return 0 if the value is NaN
191}
192
193int dms::arcsec(void) const
194{
195 if (std::isnan(D))
196 return 0;
197
198 int as = int(60.0 * (60.0 * (fabs(D) - abs(degree())) - abs(arcmin())));
199 //If the angle is slightly less than 0.0, give ArcSec a neg. sgn.
200 if (degree() == 0 && arcmin() == 0 && D < 0.0)
201 {
202 as = -1 * as;
203 }
204 return as; // Warning: Will return 0 if the value is NaN
205}
206
207int dms::marcsec(void) const
208{
209 if (std::isnan(D))
210 return 0;
211
212 int as = int(1000.0 * (60.0 * (60.0 * (fabs(D) - abs(degree())) - abs(arcmin())) - abs(arcsec())));
213 //If the angle is slightly less than 0.0, give ArcSec a neg. sgn.
214 if (degree() == 0 && arcmin() == 0 && arcsec() == 0 && D < 0.0)
215 {
216 as = -1 * as;
217 }
218 return as; // Warning: Will return 0 if the value is NaN
219}
220
221int dms::minute(void) const
222{
223 int hm = int(60.0 * (fabs(Hours()) - abs(hour())));
224 if (Hours() < 0.0 && Hours() > -1.0) //angle less than zero, but greater than -1.0
225 {
226 hm = -1 * hm; //make minute negative
227 }
228 return hm; // Warning: Will return 0 if the value is NaN
229}
230
231int dms::second(void) const
232{
233 int hs = int(60.0 * (60.0 * (fabs(Hours()) - abs(hour())) - abs(minute())));
234 if (hour() == 0 && minute() == 0 && Hours() < 0.0)
235 {
236 hs = -1 * hs;
237 }
238 return hs; // Warning: Will return 0 if the value is NaN
239}
240
241int dms::msecond(void) const
242{
243 int hs = int(1000.0 * (60.0 * (60.0 * (fabs(Hours()) - abs(hour())) - abs(minute())) - abs(second())));
244 if (hour() == 0 && minute() == 0 && second() == 0 && Hours() < 0.0)
245 {
246 hs = -1 * hs;
247 }
248 return hs; // Warning: Will return 0 if the value is NaN
249}
250
251const dms dms::reduce(void) const
252{
253 if (std::isnan(D))
254 return dms(0); // FIXME: Why 0 and not NaN? -- asimha
255
256 return dms(D - 360.0 * floor(D / 360.0));
257}
258
259double dms::reduce(const double D)
260{
261 if (std::isnan(D))
262 return D;
263
264 return (D - 360.0 * floor(D / 360.0));
265}
266
267const dms dms::deltaAngle(dms angle) const
268{
269 double angleDiff = D - angle.Degrees();
270
271 // Put in the range of [-360,360]
272 while (angleDiff > 360)
273 angleDiff -= 360;
274 while (angleDiff < -360)
275 angleDiff += 360;
276 // angleDiff in the range [180,360]
277 if (angleDiff > 180)
278 return dms(360 - angleDiff);
279 // angleDiff in the range [-360,-180]
280 else if (angleDiff < -180)
281 return dms(-(360 + angleDiff));
282 // angleDiff in the range [-180,180]
283 else
284 return dms(angleDiff);
285}
286
287const QString dms::toDMSString(const bool forceSign, const bool machineReadable, const bool highPrecision) const
288{
289 QString dummy;
290 char pm(' ');
291 QChar zero('0');
292 int dd, dm, ds;
293
294 if (machineReadable || !highPrecision)
295 // minimize the mean angle representation error of DMS format
296 // set LSD transition in the middle of +- half precision range
297 {
298 double half_precision = 1.0 / 7200.0;
299 if (Degrees() < 0.0)
300 half_precision = -half_precision;
301 dms angle(Degrees() + half_precision);
302 dd = abs(angle.degree());
303 dm = abs(angle.arcmin());
304 ds = abs(angle.arcsec());
305 }
306 else
307 {
308 dd = abs(degree());
309 dm = abs(arcmin());
310 ds = abs(arcsec());
311 }
312
313 if (Degrees() < 0.0)
314 pm = '-';
315 else if (forceSign && Degrees() > 0.0)
316 pm = '+';
317
318 if (machineReadable)
319 return QString("%1%2:%3:%4").arg(pm)
320 .arg(dd, 2, 10, QChar('0'))
321 .arg(dm, 2, 10, QChar('0'))
322 .arg(ds, 2, 10, QChar('0'));
323
324 if (highPrecision)
325 {
326 double sec = arcsec() + marcsec() / 1000.;
327 return QString("%1%2° %3\' %L4\"").arg(pm)
328 .arg(dd, 2, 10, zero)
329 .arg(dm, 2, 10, zero)
330 .arg(sec, 2,'f', 2, zero);
331 }
332
333 return QString("%1%2° %3\' %4\"").arg(pm)
334 .arg(dd, 2, 10, zero)
335 .arg(dm, 2, 10, zero)
336 .arg(ds, 2, 10, zero);
337
338#if 0
339 if (!machineReadable && dd < 10)
340 {
341 if (highPrecision)
342 {
343 double sec = arcsec() + marcsec() / 1000.;
344 return dummy.sprintf("%c%1d%c %02d\' %05.2f\"", pm, dd, 176, dm, sec);
345 }
346
347 return dummy.sprintf("%c%1d%c %02d\' %02d\"", pm, dd, 176, dm, ds);
348 }
349
350 if (!machineReadable && dd < 100)
351 {
352 if (highPrecision)
353 {
354 double sec = arcsec() + marcsec() / 1000.;
355 return dummy.sprintf("%c%2d%c %02d\' %05.2f\"", pm, dd, 176, dm, sec);
356 }
357
358 return dummy.sprintf("%c%2d%c %02d\' %02d\"", pm, dd, 176, dm, ds);
359 }
360 if (machineReadable && dd < 100)
361 return dummy.sprintf("%c%02d:%02d:%02d", pm, dd, dm, ds);
362
363 if (!machineReadable)
364 {
365 if (highPrecision)
366 {
367 double sec = arcsec() + marcsec() / 1000.;
368 return dummy.sprintf("%c%3d%c %02d\' %05.2f\"", pm, dd, 176, dm, sec);
369 }
370 else
371 return dummy.sprintf("%c%3d%c %02d\' %02d\"", pm, dd, 176, dm, ds);
372 }
373 else
374 return dummy.sprintf("%c%03d:%02d:%02d", pm, dd, dm, ds);
375#endif
376}
377
378const QString dms::toHMSString(const bool machineReadable, const bool highPrecision) const
379{
380 QChar zero('0');
381 dms angle;
382 int hh, hm, hs;
383
384 if (machineReadable || !highPrecision)
385 // minimize the mean angle representation error of HMS format
386 // set LSD transition in the middle of +- half precision range
387 {
388 double half_precision = 15.0 / 7200.0;
389 angle.setD(Degrees() + half_precision);
390 hh = angle.hour();
391 hm = angle.minute();
392 hs = angle.second();
393 }
394
395 if (machineReadable)
396 return QString("%1:%2:%3").arg(hh, 2, 10, zero)
397 .arg(hm, 2, 10, zero)
398 .arg(hs, 2, 10, zero);
399
400 if (highPrecision)
401 {
402 double sec = second() + msecond() / 1000.;
403 return QString("%1h %2m %L3s").arg(hour(), 2, 10, zero)
404 .arg(minute(), 2, 10, zero)
405 .arg(sec, 2, 'f', 2, zero);
406 }
407
408 return QString("%1h %2m %3s").arg(hh, 2, 10, zero)
409 .arg(hm, 2, 10, zero)
410 .arg(hs, 2, 10, zero);
411
412#if 0
413 QString dummy;
414 if (!machineReadable)
415 {
416 if (highPrecision)
417 {
418 double sec = second() + msecond() / 1000.;
419 return dummy.sprintf("%02dh %02dm %05.2f", hour(), minute(), sec);
420 }
421 else
422 return dummy.sprintf("%02dh %02dm %02ds", hh, hm, hs);
423 }
424 else
425 return dummy.sprintf("%02d:%02d:%02d", hh, hm, hs);
426#endif
427}
428
429dms dms::fromString(const QString &st, bool deg)
430{
431 dms result;
432 result.setFromString(st, deg);
433 return result;
434 //bool ok( false );
435
436 //ok = result.setFromString( st, deg );
437
438 // if ( ok )
439 //return result;
440 // else {
441 // kDebug() << i18n( "Could Not Set Angle from string: " ) << st;
442 // return result;
443 // }
444}
445
447{
448 if (std::isnan(D))
449 return;
450
451 switch (range)
452 {
453 case MINUSPI_TO_PI:
454 D -= 360. * floor((D + 180.) / 360.);
455 break;
456 case ZERO_TO_2PI:
457 D -= 360. * floor(D / 360.);
458 }
459}
460
461QDataStream &operator<<(QDataStream &out, const dms &d)
462{
463 out << d.D;
464 return out;
465}
466
468 double D;
469 in >> D;
470 d = dms(D);
471 return in;
472}
An angle, stored as degrees, but expressible in many ways.
Definition dms.h:38
static dms fromString(const QString &s, bool deg)
Static function to create a DMS object from a QString.
Definition dms.cpp:429
double Hours() const
Definition dms.h:168
void reduceToRange(enum dms::AngleRanges range)
Reduce this angle to the given range.
Definition dms.cpp:446
virtual void setH(const double &x)
Sets floating-point value of angle, in hours.
Definition dms.h:210
int second() const
Definition dms.cpp:231
const dms reduce() const
return the equivalent angle between 0 and 360 degrees.
Definition dms.cpp:251
virtual bool setFromString(const QString &s, bool isDeg=true)
Attempt to parse the string argument as a dms value, and set the dms object accordingly.
Definition dms.cpp:48
const QString toDMSString(const bool forceSign=false, const bool machineReadable=false, const bool highPrecision=false) const
Definition dms.cpp:287
const dms deltaAngle(dms angle) const
deltaAngle Return the shortest difference (path) between this angle and the supplied angle.
Definition dms.cpp:267
int minute() const
Definition dms.cpp:221
const QString toHMSString(const bool machineReadable=false, const bool highPrecision=false) const
Definition dms.cpp:378
int msecond() const
Definition dms.cpp:241
int marcsec() const
Definition dms.cpp:207
AngleRanges
an enum defining standard angle ranges
Definition dms.h:357
dms()
Default constructor.
Definition dms.h:41
int degree() const
Definition dms.h:116
int arcmin() const
Definition dms.cpp:180
int arcsec() const
Definition dms.cpp:193
int hour() const
Definition dms.h:147
virtual void setD(const double &x)
Sets floating-point value of angle, in degrees.
Definition dms.h:179
const double & Degrees() const
Definition dms.h:141
KCALENDARCORE_EXPORT QDataStream & operator>>(QDataStream &in, const KCalendarCore::Alarm::Ptr &)
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 & remove(QChar ch, Qt::CaseSensitivity cs)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
QStringList split(QChar sep, Qt::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
double toDouble(bool *ok) const const
int toInt(bool *ok, int base) const const
QString trimmed() const const
SkipEmptyParts
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Oct 4 2024 12:01:01 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.