~ubuntu-branches/ubuntu/precise/stellarium/precise

« back to all changes in this revision

Viewing changes to src/core/StelUtils.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Cédric Delfosse
  • Date: 2009-03-13 20:07:22 UTC
  • mfrom: (1.1.8 upstream) (4.1.2 squeeze)
  • Revision ID: james.westby@ubuntu.com-20090313200722-l66s4zy2s3e8up0s
Tags: 0.10.2-1
New upstream release

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * Stellarium
 
3
 * Copyright (C) 2002 Fabien Chereau
 
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., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
 
18
 */
 
19
 
 
20
#include <config.h>
 
21
 
 
22
#include <cmath> // std::fmod
 
23
 
 
24
#ifdef CYGWIN
 
25
 #include <malloc.h>
 
26
#endif
 
27
 
 
28
#include "StelUtils.hpp"
 
29
#include "VecMath.hpp"
 
30
#include "GLee.h"
 
31
 
 
32
#include "fixx11h.h"
 
33
#include <QString>
 
34
#include <QStringList>
 
35
#include <QTextStream>
 
36
#include <QFile>
 
37
#include <QDebug>
 
38
#include <QLocale>
 
39
#include <QRegExp>
 
40
 
 
41
namespace StelUtils
 
42
{
 
43
 
 
44
double hmsToRad(unsigned int h, unsigned int m, double s )
 
45
{
 
46
        return (double)M_PI/24.*h*2.+(double)M_PI/12.*m/60.+s*M_PI/43200.;
 
47
}
 
48
 
 
49
double dmsToRad(int d, unsigned int m, double s)
 
50
{
 
51
        if (d>=0)
 
52
                return (double)M_PI/180.*d+(double)M_PI/10800.*m+s*M_PI/648000.;
 
53
        return (double)M_PI/180.*d-(double)M_PI/10800.*m-s*M_PI/648000.;
 
54
}
 
55
 
 
56
/*************************************************************************
 
57
 Convert an angle in radian to hms
 
58
*************************************************************************/
 
59
void radToHms(double angle, unsigned int& h, unsigned int& m, double& s)
 
60
{
 
61
        angle = std::fmod(angle,2.0*M_PI);
 
62
        if (angle < 0.0) angle += 2.0*M_PI; // range: [0..2.0*M_PI)
 
63
                
 
64
        angle *= 12./M_PI;
 
65
 
 
66
        h = (unsigned int)angle;
 
67
        m = (unsigned int)((angle-h)*60);
 
68
        s = (angle-h)*3600.-60.*m;
 
69
}
 
70
 
 
71
/*************************************************************************
 
72
 Convert an angle in radian to dms
 
73
*************************************************************************/
 
74
void radToDms(double angle, bool& sign, unsigned int& d, unsigned int& m, double& s)
 
75
{
 
76
        angle = std::fmod(angle,2.0*M_PI);
 
77
        sign=true;
 
78
        if (angle<0)
 
79
        {
 
80
                angle *= -1;
 
81
                sign = false;
 
82
        }
 
83
        angle *= 180./M_PI;
 
84
        
 
85
        d = (unsigned int)angle;
 
86
        m = (unsigned int)((angle - d)*60);
 
87
        s = (angle-d)*3600-60*m;
 
88
}
 
89
 
 
90
/*************************************************************************
 
91
 Convert an angle in radian to a hms formatted string
 
92
 If the minute and second part are null are too small, don't print them
 
93
*************************************************************************/
 
94
QString radToHmsStrAdapt(double angle)
 
95
{
 
96
        unsigned int h,m;
 
97
        double s;
 
98
        QString buf;
 
99
        QTextStream ts(&buf);
 
100
        StelUtils::radToHms(angle+0.005*M_PI/12/(60*60), h, m, s);
 
101
        ts << h << 'h';
 
102
        if (std::fabs(s*100-(int)s*100)>=1)
 
103
        {
 
104
                ts << m << 'm';
 
105
                ts.setRealNumberNotation(QTextStream::FixedNotation);
 
106
                ts.setPadChar('0');
 
107
                ts.setFieldWidth(4);
 
108
                ts.setRealNumberPrecision(1);
 
109
                ts << s;
 
110
                ts.reset();
 
111
                ts << 's';
 
112
        }
 
113
        else if ((int)s!=0)
 
114
        {
 
115
                ts << m << 'm' << (int)s << 's';
 
116
        }
 
117
        else if (m!=0)
 
118
        {
 
119
                ts << m << 'm';
 
120
        }
 
121
        return buf;
 
122
}
 
123
 
 
124
/*************************************************************************
 
125
 Convert an angle in radian to a hms formatted string
 
126
 If decimal is true,  output should be like this: "  16h29m55.3s"
 
127
 If decimal is true,  output should be like this: "  16h20m0.4s"
 
128
 If decimal is false, output should be like this: "0h26m5s"
 
129
*************************************************************************/
 
130
QString radToHmsStr(double angle, bool decimal)
 
131
{
 
132
        unsigned int h,m;
 
133
        double s;
 
134
        StelUtils::radToHms(angle+0.005*M_PI/12/(60*60), h, m, s);
 
135
        int width, precision;
 
136
        QString carry;
 
137
        if (decimal)
 
138
        {
 
139
                width=4;
 
140
                precision=1;
 
141
                carry="60.0";
 
142
        }
 
143
        else
 
144
        {
 
145
                width=2;
 
146
                precision=0;
 
147
                carry="60";
 
148
        }
 
149
 
 
150
        // handle carry case (when seconds are rounded up)
 
151
        if (QString("%1").arg(s, 0, 'f', precision) == carry)
 
152
        {
 
153
                s=0;
 
154
                m+=1;
 
155
        }
 
156
        if (m==60)
 
157
        {
 
158
                m=0;
 
159
                h+=1;
 
160
        }
 
161
        if (h==24 && m==0 && s==0) 
 
162
                h=0;
 
163
 
 
164
        return QString("%1h%2m%3s").arg(h, width).arg(m).arg(s, 0, 'f', precision);
 
165
}
 
166
 
 
167
/*************************************************************************
 
168
 Convert an angle in radian to a dms formatted string
 
169
 If the minute and second part are null are too small, don't print them
 
170
*************************************************************************/
 
171
QString radToDmsStrAdapt(double angle, bool useD)
 
172
{
 
173
        QChar degsign('d');
 
174
        if (!useD)
 
175
        {
 
176
                degsign = 0x00B0;
 
177
        }
 
178
        bool sign;
 
179
        unsigned int d,m;
 
180
        double s;
 
181
        StelUtils::radToDms(angle+0.005*M_PI/180/(60*60)*(angle<0?-1.:1.), sign, d, m, s);
 
182
        QString str;
 
183
        QTextStream os(&str);
 
184
        
 
185
        os << (sign?'+':'-') << d << degsign;
 
186
        if (std::fabs(s*100-(int)s*100)>=1)
 
187
        {
 
188
                os << m << '\'' << fixed << qSetRealNumberPrecision(2) << qSetFieldWidth(5) << qSetPadChar('0') << s << qSetFieldWidth(0) << '\"';
 
189
        }
 
190
        else if ((int)s!=0)
 
191
        {
 
192
                os << m << '\'' << (int)s << '\"';
 
193
        }
 
194
        else if (m!=0)
 
195
        {
 
196
                os << m << '\'';
 
197
        }
 
198
        //qDebug() << "radToDmsStrAdapt(" << angle << ", " << useD << ") = " << str;
 
199
        return str;
 
200
}
 
201
 
 
202
 
 
203
/*************************************************************************
 
204
 Convert an angle in radian to a dms formatted string
 
205
*************************************************************************/
 
206
QString radToDmsStr(double angle, bool decimal, bool useD)
 
207
{
 
208
        QChar degsign('d');
 
209
        if (!useD)
 
210
        {
 
211
                degsign = 0x00B0;
 
212
        }
 
213
        bool sign;
 
214
        unsigned int d,m;
 
215
        double s;
 
216
        StelUtils::radToDms(angle+0.005*M_PI/180/(60*60)*(angle<0?-1.:1.), sign, d, m, s);
 
217
        QString str;
 
218
        QTextStream os(&str);
 
219
        os << (sign?'+':'-') << d << degsign;
 
220
 
 
221
        int width = 2;
 
222
        if (decimal)
 
223
        {
 
224
                os << qSetRealNumberPrecision(1);
 
225
                width = 4;
 
226
        }
 
227
        else
 
228
        {
 
229
                os << qSetRealNumberPrecision(0);
 
230
                width = 2;
 
231
        }
 
232
                
 
233
        os << qSetFieldWidth(width) << m << qSetFieldWidth(0) << '\'' 
 
234
                << fixed << qSetFieldWidth(width) << qSetPadChar('0') << s 
 
235
                << qSetFieldWidth(0) << '\"';
 
236
 
 
237
        return str;
 
238
}
 
239
 
 
240
// Obtains a Vec3f from a string with the form x,y,z
 
241
Vec3f strToVec3f(const QStringList& s)
 
242
{
 
243
        if (s.size()<3)
 
244
                 return Vec3f(0.f,0.f,0.f);
 
245
 
 
246
        return Vec3f(s[0].toFloat(),s[1].toFloat(),s[2].toFloat());
 
247
}
 
248
 
 
249
Vec3f strToVec3f(const QString& s)
 
250
{
 
251
        return strToVec3f(s.split(","));
 
252
}
 
253
 
 
254
// Converts a Vec3f to HTML color notation.
 
255
QString vec3fToHtmlColor(const Vec3f& v)
 
256
{
 
257
        return QString("#%1%2%3")
 
258
                .arg(qMin(255, int(v[0] * 255)), 2, 16, QChar('0'))
 
259
                .arg(qMin(255, int(v[1] * 255)), 2, 16, QChar('0'))
 
260
                .arg(qMin(255, int(v[2] * 255)), 2, 16, QChar('0'));
 
261
}
 
262
 
 
263
Vec3f htmlColorToVec3f(const QString& c)
 
264
{
 
265
        Vec3f v;
 
266
        QRegExp re("^#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})$");
 
267
        if (re.exactMatch(c))
 
268
        {
 
269
                bool ok;
 
270
                int i = re.capturedTexts().at(1).toInt(&ok, 16);
 
271
                v[0] = (float)i / 255.;
 
272
                i = re.capturedTexts().at(2).toInt(&ok, 16);
 
273
                v[1] = (float)i / 255.;
 
274
                i = re.capturedTexts().at(3).toInt(&ok, 16);
 
275
                v[2] = (float)i / 255.;
 
276
        }
 
277
        else
 
278
        {
 
279
                v[0] = 0.;
 
280
                v[1] = 0.;
 
281
                v[2] = 0.;
 
282
        }
 
283
        return v;
 
284
}
 
285
 
 
286
void spheToRect(double lng, double lat, Vec3d& v)
 
287
{
 
288
        const double cosLat = cos(lat);
 
289
        v.set(cos(lng) * cosLat, sin(lng) * cosLat, sin(lat));
 
290
}
 
291
 
 
292
void spheToRect(float lng, float lat, Vec3f& v)
 
293
{
 
294
        const double cosLat = cos(lat);
 
295
        v.set(cos(lng) * cosLat, sin(lng) * cosLat, sin(lat));
 
296
}
 
297
 
 
298
void rectToSphe(double *lng, double *lat, const Vec3d& v)
 
299
{
 
300
        double r = v.length();
 
301
        *lat = asin(v[2]/r);
 
302
        *lng = atan2(v[1],v[0]);
 
303
}
 
304
 
 
305
void rectToSphe(float *lng, float *lat, const Vec3d& v)
 
306
{
 
307
        double r = v.length();
 
308
        *lat = asin(v[2]/r);
 
309
        *lng = atan2(v[1],v[0]);
 
310
}
 
311
 
 
312
void rectToSphe(float *lng, float *lat, const Vec3f& v)
 
313
{
 
314
        double r = v.length();
 
315
        *lat = asin(v[2]/r);
 
316
        *lng = atan2(v[1],v[0]);
 
317
}
 
318
 
 
319
void rectToSphe(double *lng, double *lat, const Vec3f& v)
 
320
{
 
321
        double r = v.length();
 
322
        *lat = asin(v[2]/r);
 
323
        *lng = atan2(v[1],v[0]);
 
324
}
 
325
 
 
326
double getDecAngle(const QString& str)
 
327
{
 
328
        QRegExp re1("^\\s*([\\+\\-])?\\s*(\\d+)\\s*([hHDd\xBA])\\s*(\\d+)\\s*['Mm]\\s*(\\d+(\\.\\d+)?)\\s*[\"Ss]\\s*([NSEWnsew])?\\s*$"); // DMS/HMS
 
329
        QRegExp re2("^\\s*([\\+\\-])?\\s*(\\d+(\\.\\d+)?).?([NSEWnsew])?\\s*$"); // Decimal
 
330
 
 
331
        if (re1.exactMatch(str))
 
332
        {
 
333
                bool neg = (re1.capturedTexts().at(1) == "-");
 
334
                double d = re1.capturedTexts().at(2).toDouble();
 
335
                double m = re1.capturedTexts().at(4).toDouble();
 
336
                double s = re1.capturedTexts().at(5).toDouble();
 
337
                if (re1.capturedTexts().at(3).toUpper() == "H")
 
338
                {
 
339
                        d *= 15;
 
340
                        m *= 15;
 
341
                        s *= 15;
 
342
                }
 
343
                QString cardinal = re1.capturedTexts().at(7);
 
344
                double deg = d + (m/60) + (s/3600);
 
345
                if (cardinal.toLower() == "s" || cardinal.toLower() == "w" || neg)
 
346
                        deg *= -1.;
 
347
                return (deg * 2 * M_PI / 360.);
 
348
        }
 
349
        else if (re2.exactMatch(str))
 
350
        {
 
351
                bool neg = (re2.capturedTexts().at(1) == "-");
 
352
                double deg = re2.capturedTexts().at(2).toDouble();
 
353
                QString cardinal = re2.capturedTexts().at(4);
 
354
                if (cardinal.toLower() == "s" || cardinal.toLower() == "w" || neg)
 
355
                        deg *= -1.;
 
356
                return (deg * 2 * M_PI / 360.);
 
357
        }
 
358
 
 
359
        qDebug() << "getDecAngle failed to parse angle string:" << str;
 
360
        return -0.0;
 
361
}
 
362
 
 
363
// Check if a number is a power of 2
 
364
bool isPowerOfTwo(int value)
 
365
{
 
366
        return (value & -value) == value;
 
367
}
 
368
 
 
369
// Return the first power of two bigger than the given value 
 
370
int getBiggerPowerOfTwo(int value)
 
371
{
 
372
        int p=1;
 
373
        while (p<value)
 
374
                p<<=1;
 
375
        return p;
 
376
}
 
377
        
 
378
// Return the inverse sinus hyperbolic of z
 
379
double asinh(double z)
 
380
{
 
381
        return std::log(z+std::sqrt(z*z+1));
 
382
}
 
383
 
 
384
/*************************************************************************
 
385
 Convert a QT QDateTime class to julian day
 
386
*************************************************************************/
 
387
double qDateTimeToJd(const QDateTime& dateTime)
 
388
{
 
389
        return (double)(dateTime.date().toJulianDay())+(double)1./(24*60*60*1000)*QTime().msecsTo(dateTime.time())-0.5;
 
390
}
 
391
 
 
392
QDateTime jdToQDateTime(const double& jd)
 
393
{
 
394
        int year, month, day;
 
395
        getDateFromJulianDay(jd, &year, &month, &day);
 
396
        QDateTime result = QDateTime::fromString(QString("%1.%2.%3").arg(year, 4, 10, QLatin1Char('0')).arg(month).arg(day), "yyyy.M.d");
 
397
        result.setTime(jdFractionToQTime(jd));
 
398
        return result;
 
399
}
 
400
 
 
401
// based on QDateTime's original handling, but expanded to handle 0.0 and earlier.
 
402
void getDateFromJulianDay(double jd, int *year, int *month, int *day)
 
403
{
 
404
        int y, m, d;
 
405
 
 
406
        // put us in the right calendar day for the time of day.
 
407
        double fraction = jd - floor(jd);
 
408
        if (fraction >= .5)
 
409
        {
 
410
                jd += 1.0;
 
411
        }
 
412
 
 
413
        if (jd >= 2299161)
 
414
        {
 
415
                // Gregorian calendar starting from October 15, 1582
 
416
                // This algorithm is from Henry F. Fliegel and Thomas C. Van Flandern
 
417
                qulonglong ell, n, i, j;
 
418
                ell = qulonglong(floor(jd)) + 68569;
 
419
                n = (4 * ell) / 146097;
 
420
                ell = ell - (146097 * n + 3) / 4;
 
421
                i = (4000 * (ell + 1)) / 1461001;
 
422
                ell = ell - (1461 * i) / 4 + 31;
 
423
                j = (80 * ell) / 2447;
 
424
                d = ell - (2447 * j) / 80;
 
425
                ell = j / 11;
 
426
                m = j + 2 - (12 * ell);
 
427
                y = 100 * (n - 49) + i + ell;
 
428
        }
 
429
        else
 
430
        {
 
431
                // Julian calendar until October 4, 1582
 
432
                // Algorithm from Frequently Asked Questions about Calendars by Claus Toendering
 
433
                int julianDay = (int)floor(jd);
 
434
                julianDay += 32082;
 
435
                int dd = (4 * julianDay + 3) / 1461;
 
436
                int ee = julianDay - (1461 * dd) / 4;
 
437
                int mm = ((5 * ee) + 2) / 153;
 
438
                d = ee - (153 * mm + 2) / 5 + 1;
 
439
                m = mm + 3 - 12 * (mm / 10);
 
440
                y = dd - 4800 + (mm / 10);
 
441
        }
 
442
        *year = y;
 
443
        *month = m;
 
444
        *day = d;
 
445
}
 
446
 
 
447
void getTimeFromJulianDay(double julianDay, int *hour, int *minute, int *second)
 
448
{
 
449
        double frac = julianDay - (floor(julianDay));
 
450
        int s = (int)floor(frac * 24 * 60 * 60);
 
451
 
 
452
        *hour = ((s / (60 * 60))+12)%24;
 
453
        *minute = (s/(60))%60;
 
454
        *second = s % 60;
 
455
}
 
456
 
 
457
QString sixIntsToIsoString( int year, int month, int day, int hour, int minute, int second )
 
458
{
 
459
        // formatting a negative doesnt work the way i expect
 
460
 
 
461
        QString dt = QString("%1-%2-%3T%4:%5:%6")
 
462
                     .arg((year >= 0 ? year : -1* year),4,10,QLatin1Char('0'))
 
463
                     .arg(month,2,10,QLatin1Char('0'))
 
464
                     .arg(day,2,10,QLatin1Char('0'))
 
465
                     .arg(hour,2,10,QLatin1Char('0'))
 
466
                     .arg(minute,2,10,QLatin1Char('0'))
 
467
                     .arg(second,2,10,QLatin1Char('0'));
 
468
 
 
469
        if (year < 0)
 
470
        {
 
471
                dt.prepend("-");
 
472
        }
 
473
        return dt;
 
474
}
 
475
 
 
476
QString jdToIsoString(double jd)
 
477
{
 
478
        int year, month, day, hour, minute, second;
 
479
        getDateFromJulianDay(jd, &year, &month, &day);
 
480
        getTimeFromJulianDay(jd, &hour, &minute, &second);
 
481
 
 
482
        return sixIntsToIsoString(year, month, day, hour, minute, second);
 
483
}
 
484
 
 
485
// Format the date per the fmt.
 
486
QString localeDateString(int year, int month, int day, int dayOfWeek, QString fmt)
 
487
{
 
488
        /* we have to handle the year zero, and the years before qdatetime can represent. */
 
489
        const QLatin1Char quote('\'');
 
490
        QString out;
 
491
        int quotestartedat = -1;
 
492
 
 
493
        for (int i = 0; i < (int)fmt.length(); i++)
 
494
        {
 
495
                if (fmt.at(i) == quote)
 
496
                {
 
497
                        if (quotestartedat >= 0)
 
498
                        {
 
499
                                if ((quotestartedat+1) == i)
 
500
                                {
 
501
                                        out += quote;
 
502
                                        quotestartedat = -1;
 
503
                                }
 
504
                                else
 
505
                                {
 
506
                                        quotestartedat = -1;
 
507
                                }
 
508
                        }
 
509
                        else
 
510
                        {
 
511
                                quotestartedat = i;
 
512
                        }
 
513
                }
 
514
                else if (quotestartedat > 0)
 
515
                {
 
516
                        out += fmt.at(i);
 
517
                }
 
518
                else if (fmt.at(i) == QLatin1Char('d') ||
 
519
                         fmt.at(i) == QLatin1Char('M') ||
 
520
                         fmt.at(i) == QLatin1Char('y'))
 
521
                {
 
522
                        int j = i+1;
 
523
                        while ((j < fmt.length()) && (fmt.at(j) == fmt.at(i)) && (4 >= (j-i+1)))
 
524
                        {
 
525
                                j++;
 
526
                        }
 
527
 
 
528
                        QString frag = fmt.mid(i,(j-i));
 
529
 
 
530
                        if (frag == "d")
 
531
                        {
 
532
                                out += QString("%1").arg(day);
 
533
                        }
 
534
                        else if (frag == "dd")
 
535
                        {
 
536
                                out += QString("%1").arg(day, 2, 10, QLatin1Char('0'));
 
537
                        }
 
538
                        else if (frag == "ddd")
 
539
                        {
 
540
                                out += QDate::shortDayName(dayOfWeek+1);
 
541
                        }
 
542
                        else if (frag == "dddd")
 
543
                        {
 
544
                                out += QDate::longDayName(dayOfWeek+1);
 
545
                        }
 
546
                        else if (frag == "M")
 
547
                        {
 
548
                                out += QString("%1").arg(month);
 
549
                        }
 
550
                        else if (frag == "MM")
 
551
                        {
 
552
                                out += QString("%1").arg(month, 2, 10, QLatin1Char('0'));
 
553
                        }
 
554
                        else if (frag == "MMM")
 
555
                        {
 
556
                                out += QDate::shortMonthName(month);
 
557
                        }
 
558
                        else if (frag == "MMMM")
 
559
                        {
 
560
                                out += QDate::longMonthName(month);
 
561
                        }
 
562
                        else if (frag == "y")
 
563
                        {
 
564
                                out += frag;
 
565
                        }
 
566
                        else if (frag == "yy")
 
567
                        {
 
568
                                int dispyear = year % 100;
 
569
                                out += QString("%1").arg(dispyear,2,10,QLatin1Char('0'));
 
570
                        }
 
571
                        else if (frag == "yyy")
 
572
                        {
 
573
                                // assume greedy: understand yy before y.
 
574
                                int dispyear = year % 100;
 
575
                                out += QString("%1").arg(dispyear,2,10,QLatin1Char('0'));
 
576
                                out += QLatin1Char('y');
 
577
                        }
 
578
                        else if (frag == "yyyy")
 
579
                        {
 
580
                                int dispyear = (year >= 0 ? year : -1 * year);
 
581
                                if (year <  0)
 
582
                                {
 
583
                                        out += QLatin1Char('-');
 
584
                                }
 
585
                                out += QString("%1").arg(dispyear,4,10,QLatin1Char('0'));
 
586
                        }
 
587
 
 
588
                        i = j-1;
 
589
                }
 
590
                else
 
591
                {
 
592
                        out += fmt.at(i);
 
593
                }
 
594
 
 
595
 
 
596
        }
 
597
 
 
598
        return out;
 
599
}
 
600
 
 
601
//! try to get a reasonable locale date string from the system, trying to work around
 
602
//! limitations of qdatetime for large dates in the past.  see QDateTime::toString().
 
603
QString localeDateString(int year, int month, int day, int dayOfWeek)
 
604
{
 
605
 
 
606
        // try the QDateTime first
 
607
        QDate test(year, month, day);
 
608
 
 
609
        // try to avoid QDate's non-astronomical time here, don't do BCE or year 0.
 
610
        if (year > 0 && test.isValid() && !test.toString(Qt::LocaleDate).isEmpty())
 
611
        {
 
612
                return test.toString(Qt::LocaleDate);
 
613
        }
 
614
        else
 
615
        {
 
616
                return localeDateString(year,month,day,dayOfWeek,QLocale().dateFormat(QLocale::ShortFormat));
 
617
        }
 
618
}
 
619
 
 
620
 
 
621
//! use QDateTime to get a Julian Date from the system's current time.
 
622
//! this is an acceptable use of QDateTime because the system's current
 
623
//! time is more than likely always going to be expressible by QDateTime.
 
624
double getJDFromSystem(void)
 
625
{
 
626
        return qDateTimeToJd(QDateTime::currentDateTime().toUTC());
 
627
}
 
628
 
 
629
double qTimeToJDFraction(const QTime& time)
 
630
{
 
631
        return (double)1./(24*60*60*1000)*QTime().msecsTo(time)-0.5;
 
632
}
 
633
 
 
634
QTime jdFractionToQTime(const double jd)
 
635
{
 
636
        double decHours = std::fmod(jd+0.5, 1.0);
 
637
        int hours = (int)(decHours/0.041666666666666666666);
 
638
        int mins = (int)((decHours-(hours*0.041666666666666666666))/0.00069444444444444444444);
 
639
        return QTime::fromString(QString("%1.%2").arg(hours).arg(mins), "h.m");
 
640
}
 
641
 
 
642
// Use Qt's own sense of time and offset instead of platform specific code.
 
643
float getGMTShiftFromQT(double JD)
 
644
{
 
645
        int year, month, day, hour, minute, second;
 
646
        getDateFromJulianDay(JD, &year, &month, &day);
 
647
        getTimeFromJulianDay(JD, &hour, &minute, &second);
 
648
        // as analogous to second statement in getJDFromDate, nkerr
 
649
        if ( year <= 0 ) {
 
650
          year = year - 1;
 
651
        }
 
652
        QDateTime current(QDate(year, month, day), QTime(hour, minute, second));
 
653
        if (! current.isValid())
 
654
        {
 
655
                //qWarning() << "JD " << QString("%1").arg(JD) << " out of bounds of QT help with GMT shift, using current datetime";
 
656
                // Assumes the GMT shift was always the same before year -4710
 
657
                current = QDateTime(QDate(-4710, month, day), QTime(hour, minute, second));
 
658
        }
 
659
        QDateTime c1 = QDateTime::fromString(current.toString(Qt::ISODate),Qt::ISODate);
 
660
        QDateTime u1 = QDateTime::fromString(current.toUTC().toString(Qt::ISODate),Qt::ISODate);
 
661
 
 
662
        int secsto = u1.secsTo(c1);
 
663
        float hrsto = secsto / 3600.0f;
 
664
        return hrsto;
 
665
}
 
666
 
 
667
// UTC !
 
668
bool getJDFromDate(double* newjd, int y, int m, int d, int h, int min, int s)
 
669
{
 
670
        double deltaTime = (h / 24.0) + (min / (24.0*60.0)) + (s / (24.0 * 60.0 * 60.0)) - 0.5;
 
671
        QDate test((y <= 0 ? y-1 : y), m, d);
 
672
        // if QDate will oblige, do so.
 
673
        if ( test.isValid() )
 
674
        {
 
675
                double qdjd = (double)test.toJulianDay();
 
676
                qdjd += deltaTime;
 
677
                *newjd = qdjd;
 
678
                return true;
 
679
        } else
 
680
        {
 
681
                double jd = (double)((1461 * (y + 4800 + (m - 14) / 12)) / 4 + (367 * (m - 2 - 12 * ((m - 14) / 12))) / 12 - (3 * ((y + 4900 + (m - 14) / 12) / 100)) / 4 + d - 32075) - 38;
 
682
                jd += deltaTime;
 
683
                *newjd = jd;
 
684
                return true;
 
685
        }
 
686
        return false;
 
687
}
 
688
 
 
689
double getJDFromDate_alg2(int y, int m, int d, int h, int min, int s)
 
690
{
 
691
        double extra = (100.0* y) + m - 190002.5;
 
692
        double rjd = 367.0 * y;
 
693
        rjd -= floor(7.0*(y+floor((m+9.0)/12.0))/4.0);
 
694
        rjd += floor(275.0*m/9.0) ;
 
695
        rjd += d;
 
696
        rjd += (h + (min + s/60.0)/60.)/24.0;
 
697
        rjd += 1721013.5;
 
698
        rjd -= 0.5*extra/std::fabs(extra);
 
699
        rjd += 0.5;
 
700
        return rjd;
 
701
}
 
702
 
 
703
int numberOfDaysInMonthInYear(int month, int year)
 
704
{
 
705
        switch(month)
 
706
        {
 
707
                case 1:
 
708
                case 3:
 
709
                case 5:
 
710
                case 7:
 
711
                case 8:
 
712
                case 10:
 
713
                case 12:
 
714
                        return 31;
 
715
                        break;
 
716
                case 4:
 
717
                case 6:
 
718
                case 9:
 
719
                case 11:
 
720
                        return 30;
 
721
                        break;
 
722
                
 
723
                case 2:
 
724
                        if ( year > 1582 )
 
725
                        {
 
726
                                if ( year % 4 == 0 )
 
727
                                {
 
728
                                        if ( year % 100 == 0 )
 
729
                                        {
 
730
                                                if ( year % 400 == 0 )
 
731
                                                {
 
732
                                                        return 29;
 
733
                                                }
 
734
                                                else
 
735
                                                {
 
736
                                                        return 28;
 
737
                                                }
 
738
                                        }
 
739
                                        else
 
740
                                        {
 
741
                                                return 29;
 
742
                                        }
 
743
                                }
 
744
                                else
 
745
                                {
 
746
                                        return 28;
 
747
                                }
 
748
                        }
 
749
                        else
 
750
                        {
 
751
                                if ( year % 4 == 0 )
 
752
                                {
 
753
                                        return 29;
 
754
                                }
 
755
                                else
 
756
                                {
 
757
                                        return 28;
 
758
                                }
 
759
                        }
 
760
                        break;
 
761
                
 
762
                case 0:
 
763
                        return numberOfDaysInMonthInYear(12, year-1);
 
764
                        break;
 
765
                case 13:
 
766
                        return numberOfDaysInMonthInYear(1, year+1);
 
767
                        break;
 
768
                default:
 
769
                        break;
 
770
        }
 
771
        
 
772
        return 0;
 
773
}
 
774
 
 
775
//! given the submitted year/month/day hour:minute:second, try to
 
776
//! normalize into an actual year/month/day.  values can be positive, 0,
 
777
//! or negative.  start assessing from seconds to larger increments.
 
778
bool changeDateTimeForRollover(int oy, int om, int od, int oh, int omin, int os,
 
779
                                int* ry, int* rm, int* rd, int* rh, int* rmin, int* rs)
 
780
{
 
781
        bool change = false;
 
782
        
 
783
        while ( os > 59 ) {
 
784
                os -= 60;
 
785
                omin += 1;
 
786
                change = true;
 
787
        }
 
788
        while ( os < 0 ) {
 
789
                os += 60;
 
790
                omin -= 1;
 
791
                change = true;
 
792
        }
 
793
        
 
794
        while (omin > 59 ) {
 
795
                omin -= 60;
 
796
                oh += 1;
 
797
                change = true;
 
798
        }
 
799
        while (omin < 0 ) {
 
800
                omin += 60;
 
801
                oh -= 1;
 
802
                change = true;
 
803
        }
 
804
        
 
805
        while ( oh > 23 ) {
 
806
                oh -= 24;
 
807
                od += 1;
 
808
                change = true;
 
809
        }
 
810
        while ( oh < 0 ) {
 
811
                oh += 24;
 
812
                od -= 1;
 
813
                change = true;
 
814
        }
 
815
        
 
816
        while ( od > numberOfDaysInMonthInYear(om, oy) ) {
 
817
                od -= numberOfDaysInMonthInYear(om, oy);
 
818
                om++;
 
819
                if ( om > 12 ) {
 
820
                om -= 12;
 
821
                oy += 1;
 
822
                }
 
823
                change = true;
 
824
        }
 
825
        while ( od < 1 ) {
 
826
                od += numberOfDaysInMonthInYear(om-1,oy);
 
827
                om--;
 
828
                if ( om < 1 ) {
 
829
                om += 12;
 
830
                oy -= 1;
 
831
                }
 
832
                change = true;
 
833
        }
 
834
        
 
835
        while ( om > 12 ) {
 
836
                om -= 12;
 
837
                oy += 1;
 
838
                change = true;
 
839
        }
 
840
        while ( om < 1 ) {
 
841
                om += 12;
 
842
                oy -= 1;
 
843
                change = true;
 
844
        }
 
845
        
 
846
        // and the julian-gregorian epoch hole: round up to the 15th
 
847
        if ( oy == 1582 && om == 10 && ( od > 4 && od < 15 ) ) {
 
848
                od = 15;
 
849
                change = true;
 
850
        }
 
851
        
 
852
        if ( change ) {
 
853
                *ry = oy;
 
854
                *rm = om;
 
855
                *rd = od;
 
856
                *rh = oh;
 
857
                *rmin = omin;
 
858
                *rs = os;
 
859
        }
 
860
        return change;
 
861
}
 
862
 
 
863
void debugQVariantMap(const QVariant& m, const QString& indent, const QString& key)
 
864
{
 
865
        QVariant::Type t = m.type();
 
866
        if (t == QVariant::Map)
 
867
        {
 
868
                qDebug() << indent + key + "(map):";
 
869
                QList<QString> keys = m.toMap().keys();
 
870
                qSort(keys);
 
871
                foreach(QString k, keys)
 
872
                {
 
873
                        debugQVariantMap(m.toMap()[k], indent + "    ", k);
 
874
                }
 
875
        }
 
876
        else if (t == QVariant::List)
 
877
        {
 
878
                qDebug() << indent + key + "(list):";
 
879
                foreach(QVariant item, m.toList())
 
880
                {
 
881
                        debugQVariantMap(item, indent + "    ");
 
882
                }
 
883
        }
 
884
        else
 
885
                qDebug() << indent + key + " => " + m.toString();
 
886
}
 
887
 
 
888
 
 
889
QList<int> getIntsFromISO8601String(const QString & dt)
 
890
{
 
891
        // Represents a valid, complete date string.
 
892
        static QRegExp finalRe("(-0*[1-9][0-9]{0,5}|0+|0*[1-9][0-9]{0,5})-(0[1-9]|1[0-2])-(0[1-9]|[12][0-9]|3[10])[T ]([01][0-9]|2[0123]):([012345][0-9]):([012345][0-9])");
 
893
 
 
894
        QList<int> retval;
 
895
        if (finalRe.exactMatch(dt))
 
896
        {
 
897
                QStringList number_strings = finalRe.capturedTexts();
 
898
                bool ok;
 
899
                int v;
 
900
                for (int i = 1; i < number_strings.size(); i++)
 
901
                {
 
902
                        qWarning() << ":: at capture " << i << " got a " << number_strings[i];
 
903
                        ok = true;
 
904
                        v = number_strings[i].toInt(&ok, 10);
 
905
                        qWarning() << "  :: and it was a " << v << " " << ok;
 
906
                        if (ok)
 
907
                        {
 
908
                                retval.push_back(v);
 
909
                        }
 
910
                        else
 
911
                        {
 
912
                                retval.clear();
 
913
                                qWarning() << "StelUtils::getIntsFromISO8601String: input string failed to be an exact date at capture " << i << ", returning nothing: " << dt;
 
914
                                break;
 
915
                        }
 
916
                }
 
917
        }
 
918
        else
 
919
        {
 
920
                qWarning() << "StelUtils::getIntsFromISO8601String: input string failed to be an exact date, returning nothing: " << dt;
 
921
        }
 
922
        return retval;
 
923
}
 
924
 
 
925
 
 
926
} // end of the StelUtils namespace
 
927