1 /***************************************************************************
2                           dms.cpp  -  K Desktop Planetarium
3                              -------------------
4     begin                : Sun Feb 11 2001
5     copyright            : (C) 2001 by Jason Harris
6     email                : jharris@30doradus.org
7  ***************************************************************************/
8 
9 /***************************************************************************
10  *                                                                         *
11  *   This program is free software; you can redistribute it and/or modify  *
12  *   it under the terms of the GNU General Public License as published by  *
13  *   the Free Software Foundation; either version 2 of the License, or     *
14  *   (at your option) any later version.                                   *
15  *                                                                         *
16  ***************************************************************************/
17 
18 #include "dms.h"
19 
20 #include <QLocale>
21 
22 #include <QRegExp>
23 
24 #ifdef COUNT_DMS_SINCOS_CALLS
25 long unsigned dms::dms_constructor_calls         = 0;
26 long unsigned dms::dms_with_sincos_called        = 0;
27 long unsigned dms::trig_function_calls           = 0;
28 long unsigned dms::redundant_trig_function_calls = 0;
29 double dms::seconds_in_trig                      = 0;
30 #endif
31 
setD(const int & d,const int & m,const int & s,const int & ms)32 void dms::setD(const int &d, const int &m, const int &s, const int &ms)
33 {
34     D = (double)abs(d) + ((double)m + ((double)s + (double)ms / 1000.) / 60.) / 60.;
35     if (d < 0)
36     {
37         D = -1.0 * D;
38     }
39 #ifdef COUNT_DMS_SINCOS_CALLS
40     m_cosDirty = m_sinDirty = true;
41 #endif
42 }
43 
setH(const int & h,const int & m,const int & s,const int & ms)44 void dms::setH(const int &h, const int &m, const int &s, const int &ms)
45 {
46     D = 15.0 * ((double)abs(h) + ((double)m + ((double)s + (double)ms / 1000.) / 60.) / 60.);
47     if (h < 0)
48     {
49         D = -1.0 * D;
50     }
51 #ifdef COUNT_DMS_SINCOS_CALLS
52     m_cosDirty = m_sinDirty = true;
53 #endif
54 }
55 
setFromString(const QString & str,bool isDeg)56 bool dms::setFromString(const QString &str, bool isDeg)
57 {
58     int d(0), m(0);
59     double s(0.0);
60     bool checkValue(false), badEntry(false), negative(false);
61     QString entry = str.trimmed();
62     entry.remove(QRegExp("[hdms'\"°]"));
63 
64     //Account for localized decimal-point settings
65     //QString::toDouble() requires that the decimal symbol is "."
66     entry.replace(QLocale().decimalPoint(), ".");
67 
68     //empty entry returns false
69     if (entry.isEmpty())
70     {
71         dms::setD(NaN::d);
72         return false;
73     }
74 
75     //try parsing a simple integer
76     d = entry.toInt(&checkValue);
77     if (checkValue)
78     {
79         if (isDeg)
80             dms::setD(d, 0, 0);
81         else
82             dms::setH(d, 0, 0);
83         return true;
84     }
85 
86     //try parsing a simple double
87     double x = entry.toDouble(&checkValue);
88     if (checkValue)
89     {
90         if (isDeg)
91             dms::setD(x);
92         else
93             dms::setH(x);
94         return true;
95     }
96 
97     //try parsing multiple fields.
98     QStringList fields;
99 
100     //check for colon-delimiters or space-delimiters
101     if (entry.contains(':'))
102         fields = entry.split(':', QString::SkipEmptyParts);
103     else
104         fields = entry.split(' ', QString::SkipEmptyParts);
105 
106     //anything with one field is invalid!
107     if (fields.count() == 1)
108     {
109         dms::setD(NaN::d);
110         return false;
111     }
112 
113     //If two fields we will add a third one, and then parse with
114     //the 3-field code block. If field[1] is an int, add a third field equal to "0".
115     //If field[1] is a double, convert it to integer arcmin, and convert
116     //the remainder to integer arcsec
117     //If field[1] is neither int nor double, return false.
118     if (fields.count() == 2)
119     {
120         m = fields[1].toInt(&checkValue);
121         if (checkValue)
122             fields.append(QString("0"));
123         else
124         {
125             double mx = fields[1].toDouble(&checkValue);
126             if (checkValue)
127             {
128                 fields[1] = QString::number(int(mx));
129                 fields.append(QString::number(int(60.0 * (mx - int(mx)))));
130             }
131             else
132             {
133                 dms::setD(NaN::d);
134                 return false;
135             }
136         }
137     }
138 
139     //Now have (at least) three fields ( h/d m s );
140     //we can ignore anything after 3rd field
141     if (fields.count() >= 3)
142     {
143         //See if first two fields parse as integers, and third field as a double
144 
145         d = fields[0].toInt(&checkValue);
146         if (!checkValue)
147             badEntry = true;
148         m = fields[1].toInt(&checkValue);
149         if (!checkValue)
150             badEntry = true;
151         s = fields[2].toDouble(&checkValue);
152         if (!checkValue)
153             badEntry = true;
154 
155         //Special case: If first field is "-0", store the negative sign.
156         //(otherwise it gets dropped)
157         if (fields[0].at(0) == '-' && d == 0)
158             negative = true;
159     }
160 
161     if (!badEntry)
162     {
163         double D = (double)abs(d) + (double)abs(m) / 60. + (double)fabs(s) / 3600.;
164 
165         if (negative || d < 0 || m < 0 || s < 0)
166         {
167             D = -1.0 * D;
168         }
169 
170         if (isDeg)
171         {
172             dms::setD(D);
173         }
174         else
175         {
176             dms::setH(D);
177         }
178     }
179     else
180     {
181         dms::setD(NaN::d);
182         return false;
183     }
184 
185     return true;
186 }
187 
arcmin(void) const188 int dms::arcmin(void) const
189 {
190     if (std::isnan(D))
191         return 0;
192 
193     int am = int(float(60.0 * (fabs(D) - abs(degree()))));
194     if (D < 0.0 && D > -1.0) //angle less than zero, but greater than -1.0
195     {
196         am = -1 * am; //make minute negative
197     }
198     return am; // Warning: Will return 0 if the value is NaN
199 }
200 
arcsec(void) const201 int dms::arcsec(void) const
202 {
203     if (std::isnan(D))
204         return 0;
205 
206     int as = int(float(60.0 * (60.0 * (fabs(D) - abs(degree())) - abs(arcmin()))));
207     //If the angle is slightly less than 0.0, give ArcSec a neg. sgn.
208     if (degree() == 0 && arcmin() == 0 && D < 0.0)
209     {
210         as = -1 * as;
211     }
212     return as; // Warning: Will return 0 if the value is NaN
213 }
214 
marcsec(void) const215 int dms::marcsec(void) const
216 {
217     if (std::isnan(D))
218         return 0;
219 
220     int as = int(float(1000.0 * (60.0 * (60.0 * (fabs(D) - abs(degree())) - abs(arcmin())) - abs(arcsec()))));
221     //If the angle is slightly less than 0.0, give ArcSec a neg. sgn.
222     if (degree() == 0 && arcmin() == 0 && arcsec() == 0 && D < 0.0)
223     {
224         as = -1 * as;
225     }
226     return as; // Warning: Will return 0 if the value is NaN
227 }
228 
minute(void) const229 int dms::minute(void) const
230 {
231     int hm = int(float(60.0 * (fabs(Hours()) - abs(hour()))));
232     if (Hours() < 0.0 && Hours() > -1.0) //angle less than zero, but greater than -1.0
233     {
234         hm = -1 * hm; //make minute negative
235     }
236     return hm; // Warning: Will return 0 if the value is NaN
237 }
238 
second(void) const239 int dms::second(void) const
240 {
241     int hs = int(float(60.0 * (60.0 * (fabs(Hours()) - abs(hour())) - abs(minute()))));
242     if (hour() == 0 && minute() == 0 && Hours() < 0.0)
243     {
244         hs = -1 * hs;
245     }
246     return hs; // Warning: Will return 0 if the value is NaN
247 }
248 
msecond(void) const249 int dms::msecond(void) const
250 {
251     int hs = int(float(1000.0 * (60.0 * (60.0 * (fabs(Hours()) - abs(hour())) - abs(minute())) - abs(second()))));
252     if (hour() == 0 && minute() == 0 && second() == 0 && Hours() < 0.0)
253     {
254         hs = -1 * hs;
255     }
256     return hs; // Warning: Will return 0 if the value is NaN
257 }
258 
reduce(void) const259 const dms dms::reduce(void) const
260 {
261     if (std::isnan(D))
262         return dms(0);
263 
264     return dms(D - 360.0 * floor(D / 360.0));
265 }
266 
deltaAngle(dms angle) const267 const 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 
toDMSString(const bool forceSign,const bool machineReadable,const bool highPrecision) const287 const 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 = abs(degree());
293     int dm = abs(arcmin());
294     int ds = abs(arcsec());
295 
296     if (Degrees() < 0.0)
297         pm = '-';
298     else if (forceSign && Degrees() > 0.0)
299         pm = '+';
300 
301     if (machineReadable)
302         return QString("%1%2:%3:%4").arg(pm)
303                 .arg(dd, 2, 10, QChar('0'))
304                 .arg(dm, 2, 10, QChar('0'))
305                 .arg(ds, 2, 10, QChar('0'));
306 
307     if (highPrecision)
308     {
309         double sec = arcsec() + marcsec() / 1000.;
310         return QString("%1%2° %3\' %L4\"").arg(pm)
311                                          .arg(dd, 2, 10, zero)
312                                          .arg(dm, 2, 10, zero)
313                                          .arg(sec, 2,'f', 2, zero);
314     }
315 
316     return QString("%1%2° %3\' %4\"").arg(pm)
317                                      .arg(dd, 2, 10, zero)
318                                      .arg(dm, 2, 10, zero)
319                                      .arg(ds, 2, 10, zero);
320 
321 #if 0
322     if (!machineReadable && dd < 10)
323     {
324         if (highPrecision)
325         {
326             double sec = arcsec() + marcsec() / 1000.;
327             return dummy.sprintf("%c%1d%c %02d\' %05.2f\"", pm, dd, 176, dm, sec);
328         }
329 
330         return dummy.sprintf("%c%1d%c %02d\' %02d\"", pm, dd, 176, dm, ds);
331     }
332 
333     if (!machineReadable && dd < 100)
334     {
335         if (highPrecision)
336         {
337             double sec = arcsec() + marcsec() / 1000.;
338             return dummy.sprintf("%c%2d%c %02d\' %05.2f\"", pm, dd, 176, dm, sec);
339         }
340 
341         return dummy.sprintf("%c%2d%c %02d\' %02d\"", pm, dd, 176, dm, ds);
342     }
343     if (machineReadable && dd < 100)
344         return dummy.sprintf("%c%02d:%02d:%02d", pm, dd, dm, ds);
345 
346     if (!machineReadable)
347     {
348         if (highPrecision)
349         {
350             double sec = arcsec() + marcsec() / 1000.;
351             return dummy.sprintf("%c%3d%c %02d\' %05.2f\"", pm, dd, 176, dm, sec);
352         }
353         else
354             return dummy.sprintf("%c%3d%c %02d\' %02d\"", pm, dd, 176, dm, ds);
355     }
356     else
357         return dummy.sprintf("%c%03d:%02d:%02d", pm, dd, dm, ds);
358 #endif
359 }
360 
toHMSString(const bool machineReadable,const bool highPrecision) const361 const QString dms::toHMSString(const bool machineReadable, const bool highPrecision) const
362 {
363     QChar zero('0');
364     if (machineReadable)
365         return QString("%1:%2:%3").arg(hour(), 2, 10, zero)
366                                   .arg(minute(), 2, 10, zero)
367                                   .arg(second(), 2, 10, zero);
368 
369     if (highPrecision)
370     {
371         double sec = second() + msecond() / 1000.;
372         return QString("%1h %2m %L3s").arg(hour(), 2, 10, zero)
373                                      .arg(minute(), 2, 10, zero)
374                                      .arg(sec, 2, 'f', 2, zero);
375     }
376 
377     return QString("%1h %2m %3s").arg(hour(), 2, 10, zero)
378                                  .arg(minute(), 2, 10, zero)
379                                  .arg(second(), 2, 10, zero);
380 
381 #if 0
382     QString dummy;
383     if (!machineReadable)
384     {
385         if (highPrecision)
386         {
387             double sec = second() + msecond() / 1000.;
388             return dummy.sprintf("%02dh %02dm %05.2f", hour(), minute(), sec);
389         }
390         else
391             return dummy.sprintf("%02dh %02dm %02ds", hour(), minute(), second());
392     }
393     else
394         return dummy.sprintf("%02d:%02d:%02d", hour(), minute(), second());
395 #endif
396 }
397 
fromString(const QString & st,bool deg)398 dms dms::fromString(const QString &st, bool deg)
399 {
400     dms result;
401     result.setFromString(st, deg);
402     return result;
403     //bool ok( false );
404 
405     //ok = result.setFromString( st, deg );
406 
407     //	if ( ok )
408     //return result;
409     //	else {
410     //		kDebug() << i18n( "Could Not Set Angle from string: " ) << st;
411     //		return result;
412     //	}
413 }
414 
reduceToRange(enum dms::AngleRanges range)415 void dms::reduceToRange(enum dms::AngleRanges range)
416 {
417     if (std::isnan(D))
418         return;
419 
420     switch (range)
421     {
422         case MINUSPI_TO_PI:
423             D -= 360. * floor((D + 180.) / 360.);
424             break;
425         case ZERO_TO_2PI:
426             D -= 360. * floor(D / 360.);
427     }
428 }
429 
operator <<(QDataStream & out,const dms & d)430 QDataStream &operator<<(QDataStream &out, const dms &d)
431 {
432     out << d.D;
433     return out;
434 }
435 
operator >>(QDataStream & in,dms & d)436 QDataStream &operator>>(QDataStream &in, dms &d){
437    double D;
438    in >> D;
439    d = dms(D);
440    return in;
441 }
442 
443