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