1 /**********************************************************************************************
2     Copyright (C) 2008 Oliver Eichler <oliver.eichler@gmx.de>
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111 USA
17 
18 **********************************************************************************************/
19 #include "CMainWindow.h"
20 #include "gis/GeoMath.h"
21 #include "gis/proj_x.h"
22 #include "units/CUnitImperial.h"
23 #include "units/CUnitMetric.h"
24 #include "units/CUnitNautic.h"
25 
26 #include <QtWidgets>
27 const IUnit* IUnit::m_self = nullptr;
28 
29 const QPointF NOPOINTF(NOFLOAT, NOFLOAT);
30 const QPoint NOPOINT (NOINT, NOINT);
31 
32 IUnit::tz_mode_e IUnit::timeZoneMode = IUnit::eTZUtc;
33 IUnit::coord_format_e IUnit::coordFormat = IUnit::eCoordFormat1;
34 QByteArray IUnit::timeZone = "UTC";
35 bool IUnit::useShortFormat = false;
36 
37 const char* IUnit::tblTimezone[] =
38 {
39     "Africa/Abidjan",
40     "Africa/Accra",
41     "Africa/Addis_Ababa",
42     "Africa/Algiers",
43     "Africa/Asmara",
44     "Africa/Bamako",
45     "Africa/Bangui",
46     "Africa/Banjul",
47     "Africa/Bissau",
48     "Africa/Blantyre",
49     "Africa/Brazzaville",
50     "Africa/Bujumbura",
51     "Africa/Cairo",
52     "Africa/Casablanca",
53     "Africa/Conakry",
54     "Africa/Dakar",
55     "Africa/Dar_es_Salaam",
56     "Africa/Djibouti",
57     "Africa/Douala",
58     "Africa/El_Aaiun",
59     "Africa/Freetown",
60     "Africa/Gaborone",
61     "Africa/Harare",
62     "Africa/Johannesburg",
63     "Africa/Kampala",
64     "Africa/Khartoum",
65     "Africa/Kigali",
66     "Africa/Kinshasa",
67     "Africa/Lagos",
68     "Africa/Libreville",
69     "Africa/Lome",
70     "Africa/Luanda",
71     "Africa/Lubumbashi",
72     "Africa/Lusaka",
73     "Africa/Malabo",
74     "Africa/Maputo",
75     "Africa/Maseru",
76     "Africa/Mbabane",
77     "Africa/Mogadishu",
78     "Africa/Monrovia",
79     "Africa/Nairobi",
80     "Africa/Ndjamena",
81     "Africa/Niamey",
82     "Africa/Nouakchott",
83     "Africa/Ouagadougou",
84     "Africa/Porto-Novo",
85     "Africa/Sao_Tome",
86     "Africa/Tripoli",
87     "Africa/Tunis",
88     "Africa/Windhoek",
89     "America/Adak",
90     "America/Anguilla",
91     "America/Antigua",
92     "America/Araguaina",
93     "America/Argentina/Buenos_Aires",
94     "America/Argentina/Catamarca",
95     "America/Argentina/Cordoba",
96     "America/Argentina/Jujuy",
97     "America/Argentina/La_Rioja",
98     "America/Argentina/Mendoza",
99     "America/Argentina/Rio_Gallegos",
100     "America/Argentina/San_Juan",
101     "America/Argentina/San_Luis",
102     "America/Argentina/Tucuman",
103     "America/Argentina/Ushuaia",
104     "America/Aruba",
105     "America/Asuncion",
106     "America/Atikokan",
107     "America/Bahia",
108     "America/Barbados",
109     "America/Belem",
110     "America/Belize",
111     "America/Blanc-Sablon",
112     "America/Boa_Vista",
113     "America/Bogota",
114     "America/Boise",
115     "America/Cambridge_Bay",
116     "America/Campo_Grande",
117     "America/Cancun",
118     "America/Caracas",
119     "America/Cayenne",
120     "America/Cayman",
121     "America/Chicago",
122     "America/Chihuahua",
123     "America/Coral_Harbour",
124     "America/Costa_Rica",
125     "America/Cuiaba",
126     "America/Curacao",
127     "America/Dawson",
128     "America/Dawson_Creek",
129     "America/Denver",
130     "America/Dominica",
131     "America/Edmonton",
132     "America/Eirunepe",
133     "America/El_Salvador",
134     "America/Fortaleza",
135     "America/Glace_Bay",
136     "America/Goose_Bay",
137     "America/Grand_Turk",
138     "America/Grenada",
139     "America/Guadeloupe",
140     "America/Guatemala",
141     "America/Guayaquil",
142     "America/Guyana",
143     "America/Halifax",
144     "America/Havana",
145     "America/Hermosillo",
146     "America/Indiana/Indianapolis",
147     "America/Indiana/Knox",
148     "America/Indiana/Marengo",
149     "America/Indiana/Petersburg",
150     "America/Indiana/Vevay",
151     "America/Indiana/Vincennes",
152     "America/Indiana/Winamac",
153     "America/Inuvik",
154     "America/Iqaluit",
155     "America/Jamaica",
156     "America/Juneau",
157     "America/Kentucky/Louisville",
158     "America/Kentucky/Monticello",
159     "America/La_Paz",
160     "America/Lima",
161     "America/Los_Angeles",
162     "America/Maceio",
163     "America/Managua",
164     "America/Manaus",
165     "America/Marigot",
166     "America/Martinique",
167     "America/Mazatlan",
168     "America/Menominee",
169     "America/Merida",
170     "America/Mexico_City",
171     "America/Miquelon",
172     "America/Moncton",
173     "America/Monterrey",
174     "America/Montevideo",
175     "America/Montreal",
176     "America/Montserrat",
177     "America/Nassau",
178     "America/New_York",
179     "America/Nipigon",
180     "America/Noronha",
181     "America/North_Dakota/Center",
182     "America/North_Dakota/Salem",
183     "America/Panama",
184     "America/Pangnirtung",
185     "America/Paramaribo",
186     "America/Phoenix",
187     "America/Port-au-Prince",
188     "America/Port_of_Spain",
189     "America/Porto_Velho",
190     "America/Puerto_Rico",
191     "America/Rainy_River",
192     "America/Rankin_Inlet",
193     "America/Recife",
194     "America/Regina",
195     "America/Resolute",
196     "America/Rio_Branco",
197     "America/Santarem",
198     "America/Santiago",
199     "America/Santo_Domingo",
200     "America/Sao_Paulo",
201     "America/St_Barthelemy",
202     "America/St_Johns",
203     "America/St_Kitts",
204     "America/St_Lucia",
205     "America/St_Thomas",
206     "America/St_Vincent",
207     "America/Tegucigalpa",
208     "America/Thunder_Bay",
209     "America/Tijuana",
210     "America/Toronto",
211     "America/Tortola",
212     "America/Vancouver",
213     "America/Whitehorse",
214     "America/Winnipeg",
215     "America/Yellowknife",
216     "Ameriica/Swift_Current",
217     "Arctic/Longyearbyen",
218     "Asia/Aden",
219     "Asia/Almaty",
220     "Asia/Amman",
221     "Asia/Anadyr",
222     "Asia/Aqtau",
223     "Asia/Aqtobe",
224     "Asia/Ashgabat",
225     "Asia/Baghdad",
226     "Asia/Bahrain",
227     "Asia/Baku",
228     "Asia/Bangkok",
229     "Asia/Beirut",
230     "Asia/Bishkek",
231     "Asia/Brunei",
232     "Asia/Choibalsan",
233     "Asia/Chongqing",
234     "Asia/Colombo",
235     "Asia/Damascus",
236     "Asia/Dhaka",
237     "Asia/Dili",
238     "Asia/Dubai",
239     "Asia/Dushanbe",
240     "Asia/Gaza",
241     "Asia/Harbin",
242     "Asia/Ho_Chi_Minh",
243     "Asia/Hong_Kong",
244     "Asia/Hovd",
245     "Asia/Irkutsk",
246     "Asia/Jakarta",
247     "Asia/Jayapura",
248     "Asia/Jerusalem",
249     "Asia/Kabul",
250     "Asia/Kamchatka",
251     "Asia/Karachi",
252     "Asia/Kashgar",
253     "Asia/Katmandu",
254     "Asia/Kolkata",
255     "Asia/Krasnoyarsk",
256     "Asia/Kuala_Lumpur",
257     "Asia/Kuching",
258     "Asia/Kuwait",
259     "Asia/Macau",
260     "Asia/Magadan",
261     "Asia/Makassar",
262     "Asia/Manila",
263     "Asia/Muscat",
264     "Asia/Nicosia",
265     "Asia/Novosibirsk",
266     "Asia/Omsk",
267     "Asia/Oral",
268     "Asia/Phnom_Penh",
269     "Asia/Pontianak",
270     "Asia/Pyongyang",
271     "Asia/Qatar",
272     "Asia/Qyzylorda",
273     "Asia/Rangoon",
274     "Asia/Riyadh",
275     "Asia/Sakhalin",
276     "Asia/Samarkand",
277     "Asia/Seoul",
278     "Asia/Shanghai",
279     "Asia/Singapore",
280     "Asia/Taipei",
281     "Asia/Tashkent",
282     "Asia/Tbilisi",
283     "Asia/Tehran",
284     "Asia/Thimphu",
285     "Asia/Tokyo",
286     "Asia/Ulaanbaatar",
287     "Asia/Urumqi",
288     "Asia/Vientiane",
289     "Asia/Vladivostok",
290     "Asia/Yakutsk",
291     "Asia/Yekaterinburg",
292     "Asia/Yerevan",
293     "Atlantic/Azores",
294     "Atlantic/Bermuda",
295     "Atlantic/Canary",
296     "Atlantic/Cape_Verde",
297     "Atlantic/Faroe",
298     "Atlantic/Madeira",
299     "Atlantic/Reykjavik",
300     "Atlantic/South_Georgia",
301     "Atlantic/St_Helena",
302     "Atlantic/Stanley",
303     "Australia/Adelaide",
304     "Australia/Brisbane",
305     "Australia/Broken_Hill",
306     "Australia/Currie",
307     "Australia/Darwin",
308     "Australia/Eucla",
309     "Australia/Hobart",
310     "Australia/Lindeman",
311     "Australia/Lord_Howe",
312     "Australia/Melbourne",
313     "Australia/Perth",
314     "Australia/Sydney",
315     "Europe/Amsterdam",
316     "Europe/Andorra",
317     "Europe/Athens",
318     "Europe/Belgrade",
319     "Europe/Berlin",
320     "Europe/Bratislava",
321     "Europe/Brussels",
322     "Europe/Bucharest",
323     "Europe/Budapest",
324     "Europe/Chisinau",
325     "Europe/Copenhagen",
326     "Europe/Dublin",
327     "Europe/Gibraltar",
328     "Europe/Guernsey",
329     "Europe/Helsinki",
330     "Europe/Isle_of_Man",
331     "Europe/Istanbul",
332     "Europe/Jersey",
333     "Europe/Kaliningrad",
334     "Europe/Kiev",
335     "Europe/Lisbon",
336     "Europe/Ljubljana",
337     "Europe/London",
338     "Europe/Luxembourg",
339     "Europe/Madrid",
340     "Europe/Malta",
341     "Europe/Marienhamn",
342     "Europe/Minsk",
343     "Europe/Monaco",
344     "Europe/Moscow",
345     "Europe/Oslo",
346     "Europe/Paris",
347     "Europe/Podgorica",
348     "Europe/Prague",
349     "Europe/Riga",
350     "Europe/Rome",
351     "Europe/Samara",
352     "Europe/San_Marino",
353     "Europe/Sarajevo",
354     "Europe/Simferopol",
355     "Europe/Skopje",
356     "Europe/Sofia",
357     "Europe/Stockholm",
358     "Europe/Tallinn",
359     "Europe/Tirane",
360     "Europe/Uzhgorod",
361     "Europe/Vaduz",
362     "Europe/Vatican",
363     "Europe/Vienna",
364     "Europe/Vilnius",
365     "Europe/Volgograd",
366     "Europe/Warsaw",
367     "Europe/Zagreb",
368     "Europe/Zaporozhye",
369     "Europe/Zurich",
370     "Indian/Antananarivo",
371     "Indian/Chagos",
372     "Indian/Christmas",
373     "Indian/Cocos",
374     "Indian/Comoro",
375     "Indian/Kerguelen",
376     "Indian/Mahe",
377     "Indian/Maldives",
378     "Indian/Mauritius",
379     "Indian/Mayotte",
380     "Indian/Reunion",
381     "Pacific/Apia",
382     "Pacific/Auckland",
383     "Pacific/Chatham",
384     "Pacific/Easter",
385     "Pacific/Efate",
386     "Pacific/Enderbury",
387     "Pacific/Fakaofo",
388     "Pacific/Fiji",
389     "Pacific/Funafuti",
390     "Pacific/Galapagos",
391     "Pacific/Gambier",
392     "Pacific/Guadalcanal",
393     "Pacific/Guam",
394     "Pacific/Honolulu",
395     "Pacific/Johnston",
396     "Pacific/Kiritimati",
397     "Pacific/Kosrae",
398     "Pacific/Kwajalein",
399     "Pacific/Majuro",
400     "Pacific/Marquesas",
401     "Pacific/Midway",
402     "Pacific/Nauru",
403     "Pacific/Niue",
404     "Pacific/Norfolk",
405     "Pacific/Noumea",
406     "Pacific/Pago_Pago",
407     "Pacific/Palau",
408     "Pacific/Pitcairn",
409     "Pacific/Ponape",
410     "Pacific/Port_Moresby",
411     "Pacific/Rarotonga",
412     "Pacific/Saipan",
413     "Pacific/Tahiti",
414     "Pacific/Tarawa",
415     "Pacific/Tongatapu",
416     "Pacific/Truk",
417     "Pacific/Wake",
418     "Pacific/Wallis",
419     0
420 };
421 
422 const int N_TIMEZONES = sizeof(IUnit::tblTimezone) / sizeof(const char*);
423 
424 const QRegExp IUnit::reCoord1("^\\s*([N|S]){1}\\W*([0-9]+)\\W*([0-9]+\\.[0-9]+)\\s+([E|W|O]){1}\\W*([0-9]+)\\W*([0-9]+\\.[0-9]+)\\s*$");
425 
426 const QRegExp IUnit::reCoord2("^\\s*([N|S]){1}\\s*([0-9]+\\.[0-9]+)\\W*\\s+([E|W|O]){1}\\s*([0-9]+\\.[0-9]+)\\W*\\s*$");
427 
428 const QRegExp IUnit::reCoord3("^\\s*([-0-9]+\\.[0-9]+)\\s+([-0-9]+\\.[0-9]+)\\s*$");
429 
430 const QRegExp IUnit::reCoord4("^\\s*([N|S]){1}\\s*([0-9]+)\\W+([0-9]+)\\W+([0-9]+\\.[0-9]+)\\W*([E|W|O]){1}\\W*([0-9]+)\\W+([0-9]+)\\W+([0-9]+\\.[0-9]+)\\W*\\s*$");
431 
432 const QRegExp IUnit::reCoord5("^\\s*([-0-9]+\\.[0-9]+)([N|S])\\s+([-0-9]+\\.[0-9]+)([W|E])\\s*$");
433 
IUnit(const type_e & type,const QString & baseunit,const qreal basefactor,const QString & speedunit,const qreal speedfactor,QObject * parent)434 IUnit::IUnit(const type_e& type, const QString& baseunit, const qreal basefactor, const QString& speedunit, const qreal speedfactor, QObject* parent)
435     : QObject(parent)
436     , type(type)
437     , baseunit(baseunit)
438     , basefactor(basefactor)
439     , speedunit(speedunit)
440     , speedfactor(speedfactor)
441 {
442     //there can be only one...
443     if(nullptr != m_self)
444     {
445         delete m_self;
446     }
447     m_self = this;
448 }
449 
450 
setUnitType(type_e t,QObject * parent)451 void IUnit::setUnitType(type_e t, QObject* parent)
452 {
453     switch(t)
454     {
455     case eTypeMetric:
456         new CUnitMetric(parent);
457         break;
458 
459     case eTypeImperial:
460         new CUnitImperial(parent);
461         break;
462 
463     case eTypeNautic:
464         new CUnitNautic(parent);
465         break;
466     }
467 
468     QSettings cfg;
469     cfg.setValue("Units/type", t);
470 }
471 
meter2speed(qreal meter,QString & val,QString & unit) const472 void IUnit::meter2speed(qreal meter, QString& val, QString& unit) const
473 {
474     val.sprintf("%2.2f", meter * speedfactor);
475     unit = speedunit;
476 }
477 
seconds2time(quint32 ttime,QString & val,QString & unit) const478 void IUnit::seconds2time(quint32 ttime, QString& val, QString& unit) const
479 {
480     QTime time(0, 0, 0);
481     quint32 days = ttime / 86400;
482 
483     time = time.addSecs(ttime);
484 
485     if(days)
486     {
487         val = QString("%1:").arg(days) + time.toString("HH:mm:ss");
488         unit = "d";
489     }
490     else
491     {
492         val = time.toString("HH:mm:ss");
493         unit = "h";
494     }
495 }
496 
parseTimestamp(const QString & time,QDateTime & datetime)497 bool IUnit::parseTimestamp(const QString& time, QDateTime& datetime)
498 {
499     int tzoffset;
500     datetime = parseTimestamp(time, tzoffset);
501 
502     return datetime.isValid();
503 }
504 
505 
parseTimestamp(const QString & timetext,int & tzoffset)506 QDateTime IUnit::parseTimestamp(const QString& timetext, int& tzoffset)
507 {
508     const QRegExp tzRE("[-+]\\d\\d:\\d\\d$");
509     int i;
510 
511     tzoffset = 0;
512 
513     QString format = "yyyy-MM-dd'T'hh:mm:ss";
514 
515     i = timetext.indexOf(".");
516     if (i != NOIDX)
517     {
518         if(timetext[i + 1] == '0')
519         {
520             format += ".zzz";
521         }
522         else
523         {
524             format += ".z";
525         }
526     }
527 
528     // trailing "Z" explicitly declares the timestamp to be UTC
529     if (timetext.indexOf("Z") != NOIDX)
530     {
531         format += "'Z'";
532     }
533     else if ((i = tzRE.indexIn(timetext)) != NOIDX)
534     {
535         // trailing timezone offset [-+]HH:MM present
536         // This does not match the original intentions of the GPX
537         // file format but appears to be found occasionally in
538         // the wild.  Try our best parsing it.
539 
540         // add the literal string to the format so fromString()
541         // will succeed
542         format += "'";
543         format += timetext.rightRef(6);
544         format += "'";
545 
546         // calculate the offset
547         int offsetHours(timetext.midRef(i + 1, 2).toUInt());
548         int offsetMinutes(timetext.midRef(i + 4, 2).toUInt());
549         if (timetext[i] == '-')
550         {
551             tzoffset = -(60 * offsetHours + offsetMinutes);
552         }
553         else
554         {
555             tzoffset = 60 * offsetHours + offsetMinutes;
556         }
557         tzoffset *= 60;          // seconds
558     }
559 
560     QDateTime datetime = QDateTime::fromString(timetext, format);
561     datetime.setOffsetFromUtc(tzoffset);
562 
563     return datetime;
564 }
565 
datetime2string(const QDateTime & time,bool shortDate,const QPointF & pos)566 QString IUnit::datetime2string(const QDateTime& time, bool shortDate, const QPointF& pos)
567 {
568     QTimeZone tz;
569 
570     tz_mode_e tmpMode = (pos != NOPOINTF) ? timeZoneMode : eTZLocal;
571 
572     switch(tmpMode)
573     {
574     case eTZUtc:
575         tz = QTimeZone("UTC");
576         break;
577 
578     case eTZLocal:
579         tz = QTimeZone(QTimeZone::systemTimeZoneId());
580         break;
581 
582     case eTZAuto:
583         tz = QTimeZone(pos2timezone(pos));
584         break;
585 
586     case eTZSelected:
587         tz = QTimeZone(timeZone);
588         break;
589     }
590 
591     QDateTime tmp = time.toTimeZone(tz);
592     return tmp.toString((shortDate | useShortFormat) ? Qt::ISODate : Qt::SystemLocaleLongDate);
593 }
594 
pos2timezone(const QPointF & pos)595 QByteArray IUnit::pos2timezone(const QPointF& pos)
596 {
597     static QImage imgTimezone = QPixmap(":/pics/timezones.png").toImage();
598 
599     int x = qRound(2048.0 / 360.0 * (180.0 + pos.x() * RAD_TO_DEG));
600     int y = qRound(1024.0 / 180.0 * (90.0 - pos.y() * RAD_TO_DEG));
601 
602     QRgb rgb = imgTimezone.pixel(x, y);
603 
604     if(qRed(rgb) == 0 && qGreen(rgb) == 0)
605     {
606         return "UTC";
607     }
608 
609     int tz = ((qRed(rgb) & 248) << 1) + ((qGreen(rgb) >> 4) & 15);
610     if(tz >= N_TIMEZONES)
611     {
612         return 0;
613     }
614 
615     return tblTimezone[tz];
616 }
617 
degToStr(const qreal & x,const qreal & y,QString & str)618 bool IUnit::degToStr(const qreal& x, const qreal& y, QString& str)
619 {
620     if(x < -180 || 180 < x)
621     {
622         return false;
623     }
624 
625     if(y < -90 || 90 < y)
626     {
627         return false;
628     }
629 
630     switch(coordFormat)
631     {
632     case eCoordFormat1:
633     {
634         qint32 degN, degE;
635         qreal minN, minE;
636 
637         bool signLat = GPS_Math_Deg_To_DegMin(y, &degN, &minN);
638         bool signLon = GPS_Math_Deg_To_DegMin(x, &degE, &minE);
639 
640         const QString& lat = signLat ? "S" : "N";
641         const QString& lng = signLon ? "W" : "E";
642         str.sprintf("%s%02d° %06.3f %s%03d° %06.3f", lat.toUtf8().data(), qAbs(degN), minN, lng.toUtf8().data(), qAbs(degE), minE);
643         break;
644     }
645 
646     case eCoordFormat2:
647     {
648         const QString& lat = (y < 0) ? "S" : "N";
649         const QString& lng = (x < 0) ? "W" : "E";
650         str.sprintf("%s%02.6f° %s%03.6f°", lat.toUtf8().data(), qAbs(y), lng.toUtf8().data(), qAbs(x));
651         break;
652     }
653 
654     case eCoordFormat3:
655     {
656         qint32 degN, degE;
657         qreal minN, minE;
658 
659         bool signLat = GPS_Math_Deg_To_DegMin(y, &degN, &minN);
660         bool signLon = GPS_Math_Deg_To_DegMin(x, &degE, &minE);
661 
662         qreal secN = (minN - qFloor(minN)) * 60;
663         qreal secE = (minE - qFloor(minE)) * 60;
664 
665         const QString& lat = signLat ? "S" : "N";
666         const QString& lng = signLon ? "W" : "E";
667         str.sprintf("%s%02d° %02d' %02.2f'' %s%03d° %02d' %02.2f''", lat.toUtf8().data(), qAbs(degN), qFloor(minN), secN, lng.toUtf8().data(), qAbs(degE), qFloor(minE), secE);
668         break;
669     }
670     }
671 
672     return true;
673 }
674 
strToDeg(const QString & str,qreal & lon,qreal & lat)675 bool IUnit::strToDeg(const QString& str, qreal& lon, qreal& lat)
676 {
677     if(reCoord2.exactMatch(str))
678     {
679         bool signLat = reCoord2.cap(1) == "S";
680         qreal absLat = reCoord2.cap(2).toDouble();
681         lat = signLat ? -absLat : absLat;
682 
683         bool signLon = reCoord2.cap(3) == "W";
684         qreal absLon = reCoord2.cap(4).toDouble();
685         lon = signLon ? -absLon : absLon;
686     }
687     else if(reCoord1.exactMatch(str))
688     {
689         bool signLat = reCoord1.cap(1) == "S";
690         int degLat = reCoord1.cap(2).toInt();
691         qreal minLat = reCoord1.cap(3).toDouble();
692 
693         GPS_Math_DegMin_To_Deg(signLat, degLat, minLat, lat);
694 
695         bool signLon = reCoord1.cap(4) == "W";
696         int degLon = reCoord1.cap(5).toInt();
697         qreal minLon = reCoord1.cap(6).toDouble();
698 
699         GPS_Math_DegMin_To_Deg(signLon, degLon, minLon, lon);
700     }
701     else if(reCoord3.exactMatch(str))
702     {
703         lat = reCoord3.cap(1).toDouble();
704         lon = reCoord3.cap(2).toDouble();
705     }
706     else if(reCoord4.exactMatch(str))
707     {
708         bool signLat = reCoord4.cap(1) == "S";
709         int degLat = reCoord4.cap(2).toInt();
710         int minLat = reCoord4.cap(3).toInt();
711         qreal secLat = reCoord4.cap(4).toFloat();
712 
713         GPS_Math_DegMinSec_To_Deg(signLat, degLat, minLat, secLat, lat);
714 
715         bool signLon = reCoord4.cap(5) == "W";
716         int degLon = reCoord4.cap(6).toInt();
717         int minLon = reCoord4.cap(7).toInt();
718         qreal secLon = reCoord4.cap(8).toFloat();
719 
720         GPS_Math_DegMinSec_To_Deg(signLon, degLon, minLon, secLon, lon);
721     }
722     else if(reCoord5.exactMatch(str))
723     {
724         bool signLon = reCoord4.cap(4) == "W";
725         bool signLat = reCoord4.cap(2) == "S";
726         lat = reCoord5.cap(1).toDouble();
727         lon = reCoord5.cap(3).toDouble();
728 
729         if(signLon)
730         {
731             lon = -lon;
732         }
733         if(signLat)
734         {
735             lat = -lat;
736         }
737     }
738     else
739     {
740         QMessageBox::warning(&CMainWindow::self(), tr("Error"), tr("Bad position format. Must be: \"[N|S] ddd mm.sss [W|E] ddd mm.sss\" or \"[N|S] ddd.ddd [W|E] ddd.ddd\""), QMessageBox::Ok, QMessageBox::NoButton);
741         return false;
742     }
743 
744     if(fabs(lon) > 180.0 || fabs(lat) > 90.0)
745     {
746         QMessageBox::warning(&CMainWindow::self(), tr("Error"), tr("Position values out of bounds. "), QMessageBox::Ok, QMessageBox::NoButton);
747         return false;
748     }
749 
750     return true;
751 }
752 
isValidCoordString(const QString & str)753 bool IUnit::isValidCoordString(const QString& str)
754 {
755     if(reCoord1.exactMatch(str))
756     {
757         return true;
758     }
759     else if(reCoord2.exactMatch(str))
760     {
761         return true;
762     }
763     else if(reCoord3.exactMatch(str))
764     {
765         return true;
766     }
767     else if(reCoord4.exactMatch(str))
768     {
769         return true;
770     }
771     else if(reCoord5.exactMatch(str))
772     {
773         return true;
774     }
775     return false;
776 }
777