Kstars

dms.cpp
1 /*
2  SPDX-FileCopyrightText: 2001 Jason Harris <[email protected]>
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
17 long unsigned dms::dms_constructor_calls = 0;
18 long unsigned dms::dms_with_sincos_called = 0;
19 long unsigned dms::trig_function_calls = 0;
20 long unsigned dms::redundant_trig_function_calls = 0;
21 double dms::seconds_in_trig = 0;
22 #endif
23 
24 void 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 
36 void 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 
48 bool 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 
180 int 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 
193 int 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 
207 int 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 
221 int 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 
231 int 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 
241 int 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 
251 const dms dms::reduce(void) const
252 {
253  if (std::isnan(D))
254  return dms(0);
255 
256  return dms(D - 360.0 * floor(D / 360.0));
257 }
258 
259 const dms dms::deltaAngle(dms angle) const
260 {
261  double angleDiff = D - angle.Degrees();
262 
263  // Put in the range of [-360,360]
264  while (angleDiff > 360)
265  angleDiff -= 360;
266  while (angleDiff < -360)
267  angleDiff += 360;
268  // angleDiff in the range [180,360]
269  if (angleDiff > 180)
270  return dms(360 - angleDiff);
271  // angleDiff in the range [-360,-180]
272  else if (angleDiff < -180)
273  return dms(-(360 + angleDiff));
274  // angleDiff in the range [-180,180]
275  else
276  return dms(angleDiff);
277 }
278 
279 const QString dms::toDMSString(const bool forceSign, const bool machineReadable, const bool highPrecision) const
280 {
281  QString dummy;
282  char pm(' ');
283  QChar zero('0');
284  int dd, dm, ds;
285 
286  if (machineReadable || !highPrecision)
287  // minimize the mean angle representation error of DMS format
288  // set LSD transition in the middle of +- half precision range
289  {
290  double half_precision = 1.0 / 7200.0;
291  if (Degrees() < 0.0)
292  half_precision = -half_precision;
293  dms angle(Degrees() + half_precision);
294  dd = abs(angle.degree());
295  dm = abs(angle.arcmin());
296  ds = abs(angle.arcsec());
297  }
298  else
299  {
300  dd = abs(degree());
301  dm = abs(arcmin());
302  ds = abs(arcsec());
303  }
304 
305  if (Degrees() < 0.0)
306  pm = '-';
307  else if (forceSign && Degrees() > 0.0)
308  pm = '+';
309 
310  if (machineReadable)
311  return QString("%1%2:%3:%4").arg(pm)
312  .arg(dd, 2, 10, QChar('0'))
313  .arg(dm, 2, 10, QChar('0'))
314  .arg(ds, 2, 10, QChar('0'));
315 
316  if (highPrecision)
317  {
318  double sec = arcsec() + marcsec() / 1000.;
319  return QString("%1%2° %3\' %L4\"").arg(pm)
320  .arg(dd, 2, 10, zero)
321  .arg(dm, 2, 10, zero)
322  .arg(sec, 2,'f', 2, zero);
323  }
324 
325  return QString("%1%2° %3\' %4\"").arg(pm)
326  .arg(dd, 2, 10, zero)
327  .arg(dm, 2, 10, zero)
328  .arg(ds, 2, 10, zero);
329 
330 #if 0
331  if (!machineReadable && dd < 10)
332  {
333  if (highPrecision)
334  {
335  double sec = arcsec() + marcsec() / 1000.;
336  return dummy.sprintf("%c%1d%c %02d\' %05.2f\"", pm, dd, 176, dm, sec);
337  }
338 
339  return dummy.sprintf("%c%1d%c %02d\' %02d\"", pm, dd, 176, dm, ds);
340  }
341 
342  if (!machineReadable && dd < 100)
343  {
344  if (highPrecision)
345  {
346  double sec = arcsec() + marcsec() / 1000.;
347  return dummy.sprintf("%c%2d%c %02d\' %05.2f\"", pm, dd, 176, dm, sec);
348  }
349 
350  return dummy.sprintf("%c%2d%c %02d\' %02d\"", pm, dd, 176, dm, ds);
351  }
352  if (machineReadable && dd < 100)
353  return dummy.sprintf("%c%02d:%02d:%02d", pm, dd, dm, ds);
354 
355  if (!machineReadable)
356  {
357  if (highPrecision)
358  {
359  double sec = arcsec() + marcsec() / 1000.;
360  return dummy.sprintf("%c%3d%c %02d\' %05.2f\"", pm, dd, 176, dm, sec);
361  }
362  else
363  return dummy.sprintf("%c%3d%c %02d\' %02d\"", pm, dd, 176, dm, ds);
364  }
365  else
366  return dummy.sprintf("%c%03d:%02d:%02d", pm, dd, dm, ds);
367 #endif
368 }
369 
370 const QString dms::toHMSString(const bool machineReadable, const bool highPrecision) const
371 {
372  QChar zero('0');
373  dms angle;
374  int hh, hm, hs;
375 
376  if (machineReadable || !highPrecision)
377  // minimize the mean angle representation error of HMS format
378  // set LSD transition in the middle of +- half precision range
379  {
380  double half_precision = 15.0 / 7200.0;
381  angle.setD(Degrees() + half_precision);
382  hh = angle.hour();
383  hm = angle.minute();
384  hs = angle.second();
385  }
386 
387  if (machineReadable)
388  return QString("%1:%2:%3").arg(hh, 2, 10, zero)
389  .arg(hm, 2, 10, zero)
390  .arg(hs, 2, 10, zero);
391 
392  if (highPrecision)
393  {
394  double sec = second() + msecond() / 1000.;
395  return QString("%1h %2m %L3s").arg(hour(), 2, 10, zero)
396  .arg(minute(), 2, 10, zero)
397  .arg(sec, 2, 'f', 2, zero);
398  }
399 
400  return QString("%1h %2m %3s").arg(hh, 2, 10, zero)
401  .arg(hm, 2, 10, zero)
402  .arg(hs, 2, 10, zero);
403 
404 #if 0
405  QString dummy;
406  if (!machineReadable)
407  {
408  if (highPrecision)
409  {
410  double sec = second() + msecond() / 1000.;
411  return dummy.sprintf("%02dh %02dm %05.2f", hour(), minute(), sec);
412  }
413  else
414  return dummy.sprintf("%02dh %02dm %02ds", hh, hm, hs);
415  }
416  else
417  return dummy.sprintf("%02d:%02d:%02d", hh, hm, hs);
418 #endif
419 }
420 
421 dms dms::fromString(const QString &st, bool deg)
422 {
423  dms result;
424  result.setFromString(st, deg);
425  return result;
426  //bool ok( false );
427 
428  //ok = result.setFromString( st, deg );
429 
430  // if ( ok )
431  //return result;
432  // else {
433  // kDebug() << i18n( "Could Not Set Angle from string: " ) << st;
434  // return result;
435  // }
436 }
437 
439 {
440  if (std::isnan(D))
441  return;
442 
443  switch (range)
444  {
445  case MINUSPI_TO_PI:
446  D -= 360. * floor((D + 180.) / 360.);
447  break;
448  case ZERO_TO_2PI:
449  D -= 360. * floor(D / 360.);
450  }
451 }
452 
453 QDataStream &operator<<(QDataStream &out, const dms &d)
454 {
455  out << d.D;
456  return out;
457 }
458 
460  double D;
461  in >> D;
462  d = dms(D);
463  return in;
464 }
QString number(int n, int base)
int marcsec() const
Definition: dms.cpp:207
virtual void setD(const double &x)
Sets floating-point value of angle, in degrees.
Definition: dms.h:179
int degree() const
Definition: dms.h:116
QStringList split(const QString &sep, QString::SplitBehavior behavior, Qt::CaseSensitivity cs) const const
QString trimmed() const const
int msecond() const
Definition: dms.cpp:241
dms()
Default constructor.
Definition: dms.h:41
int arcmin() const
Definition: dms.cpp:180
const QString toHMSString(const bool machineReadable=false, const bool highPrecision=false) const
Definition: dms.cpp:370
virtual void setH(const double &x)
Sets floating-point value of angle, in hours.
Definition: dms.h:210
QString & sprintf(const char *cformat,...)
const QString toDMSString(const bool forceSign=false, const bool machineReadable=false, const bool highPrecision=false) const
Definition: dms.cpp:279
SkipEmptyParts
bool isEmpty() const const
int toInt(bool *ok, int base) const const
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
AngleRanges
an enum defining standard angle ranges
Definition: dms.h:351
int second() const
Definition: dms.cpp:231
double toDouble(bool *ok) const const
QString & replace(int position, int n, QChar after)
QString & remove(int position, int n)
An angle, stored as degrees, but expressible in many ways.
Definition: dms.h:37
QString arg(qlonglong a, int fieldWidth, int base, QChar fillChar) const const
QDebug operator<<(QDebug d, const QCPVector2D &vec)
Definition: qcustomplot.h:450
const double & Degrees() const
Definition: dms.h:141
int hour() const
Definition: dms.h:147
int arcsec() const
Definition: dms.cpp:193
const dms deltaAngle(dms angle) const
deltaAngle Return the shortest difference (path) between this angle and the supplied angle.
Definition: dms.cpp:259
void reduceToRange(enum dms::AngleRanges range)
Reduce this angle to the given range.
Definition: dms.cpp:438
const dms reduce() const
return the equivalent angle between 0 and 360 degrees.
Definition: dms.cpp:251
bool contains(QChar ch, Qt::CaseSensitivity cs) const const
int minute() const
Definition: dms.cpp:221
QDataStream & operator>>(QDataStream &in, KDateTime &dateTime)
double Hours() const
Definition: dms.h:168
static dms fromString(const QString &s, bool deg)
Static function to create a DMS object from a QString.
Definition: dms.cpp:421
This file is part of the KDE documentation.
Documentation copyright © 1996-2022 The KDE developers.
Generated on Sat Aug 13 2022 04:01:52 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.