OpenCPN Partial API docs
Loading...
Searching...
No Matches
navutil_base.cpp
1/***************************************************************************
2 *
3 * Project: OpenCPN
4 * Purpose: Navigation Utility Functions without GUI deps
5 * Author: David Register
6 *
7 ***************************************************************************
8 * Copyright (C) 2010 by David S. Register *
9 * *
10 * This program is free software; you can redistribute it and/or modify *
11 * it under the terms of the GNU General Public License as published by *
12 * the Free Software Foundation; either version 2 of the License, or *
13 * (at your option) any later version. *
14 * *
15 * This program is distributed in the hope that it will be useful, *
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
18 * GNU General Public License for more details. *
19 * *
20 * You should have received a copy of the GNU General Public License *
21 * along with this program; if not, write to the *
22 * Free Software Foundation, Inc., *
23 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
24 **************************************************************************/
25
26/**************************************************************************/
27/* Formats the coordinates to string */
28/**************************************************************************/
29
30#include <iomanip>
31#include <sstream>
32
33#include <wx/datetime.h>
34#include <wx/math.h>
35#include <wx/string.h>
36#include <wx/translation.h>
37#include <wx/utils.h>
38
39#include "model/navutil_base.h"
40#include "model/own_ship.h"
41#include "vector2D.h"
42
44static wxTimeSpan RoundToMinutes(const wxTimeSpan &span) {
45 auto minutes = span.GetMinutes() % 60;
46 auto seconds = span.GetSeconds() % 60;
47 if (seconds > 30) minutes += 1;
48 return wxTimeSpan(span.GetHours(), minutes, 0);
49}
50
51wxString toSDMM(int NEflag, double a, bool hi_precision) {
52 wxString s;
53 double mpy;
54 short neg = 0;
55 int d;
56 long m;
57 double ang = a;
58 char c = 'N';
59
60 if (a < 0.0) {
61 a = -a;
62 neg = 1;
63 }
64 d = (int)a;
65 if (neg) d = -d;
66 if (NEflag) {
67 if (NEflag == 1) {
68 c = 'N';
69
70 if (neg) {
71 d = -d;
72 c = 'S';
73 }
74 } else if (NEflag == 2) {
75 c = 'E';
76
77 if (neg) {
78 d = -d;
79 c = 'W';
80 }
81 }
82 }
83
84 switch (g_iSDMMFormat) {
85 case 0:
86 mpy = 600.0;
87 if (hi_precision) mpy = mpy * 1000;
88
89 m = (long)wxRound((a - (double)d) * mpy);
90
91 if (!NEflag || NEflag < 1 || NEflag > 2) // Does it EVER happen?
92 {
93 if (hi_precision)
94 s.Printf(_T ( "%d%c %02ld.%04ld'" ), d, 0x00B0, m / 10000, m % 10000);
95 else
96 s.Printf(_T ( "%d%c %02ld.%01ld'" ), d, 0x00B0, m / 10, m % 10);
97 } else {
98 if (hi_precision)
99 if (NEflag == 1)
100 s.Printf(_T ( "%02d%c %02ld.%04ld' %c" ), d, 0x00B0, m / 10000,
101 (m % 10000), c);
102 else
103 s.Printf(_T ( "%03d%c %02ld.%04ld' %c" ), d, 0x00B0, m / 10000,
104 (m % 10000), c);
105 else if (NEflag == 1)
106 s.Printf(_T ( "%02d%c %02ld.%01ld' %c" ), d, 0x00B0, m / 10, (m % 10),
107 c);
108 else
109 s.Printf(_T ( "%03d%c %02ld.%01ld' %c" ), d, 0x00B0, m / 10, (m % 10),
110 c);
111 }
112 break;
113 case 1:
114 if (hi_precision)
115 s.Printf(_T ( "%03.6f" ),
116 ang); // cca 11 cm - the GPX precision is higher, but as we
117 // use hi_precision almost everywhere it would be a
118 // little too much....
119 else
120 s.Printf(_T ( "%03.4f" ), ang); // cca 11m
121 break;
122 case 2:
123 m = (long)((a - (double)d) * 60);
124 mpy = 10.0;
125 if (hi_precision) mpy = mpy * 100;
126 long sec = (long)((a - (double)d - (((double)m) / 60)) * 3600 * mpy);
127
128 if (!NEflag || NEflag < 1 || NEflag > 2) // Does it EVER happen?
129 {
130 if (hi_precision)
131 s.Printf(_T ( "%d%c %ld'%ld.%ld\"" ), d, 0x00B0, m, sec / 1000,
132 sec % 1000);
133 else
134 s.Printf(_T ( "%d%c %ld'%ld.%ld\"" ), d, 0x00B0, m, sec / 10,
135 sec % 10);
136 } else {
137 if (hi_precision)
138 if (NEflag == 1)
139 s.Printf(_T ( "%02d%c %02ld' %02ld.%03ld\" %c" ), d, 0x00B0, m,
140 sec / 1000, sec % 1000, c);
141 else
142 s.Printf(_T ( "%03d%c %02ld' %02ld.%03ld\" %c" ), d, 0x00B0, m,
143 sec / 1000, sec % 1000, c);
144 else if (NEflag == 1)
145 s.Printf(_T ( "%02d%c %02ld' %02ld.%ld\" %c" ), d, 0x00B0, m,
146 sec / 10, sec % 10, c);
147 else
148 s.Printf(_T ( "%03d%c %02ld' %02ld.%ld\" %c" ), d, 0x00B0, m,
149 sec / 10, sec % 10, c);
150 }
151 break;
152 }
153 return s;
154}
155
156/**************************************************************************/
157/* Converts the speed to the units selected by user */
158/**************************************************************************/
159double toUsrSpeed(double kts_speed, int unit) {
160 double ret = NAN;
161 if (unit == -1) unit = g_iSpeedFormat;
162 switch (unit) {
163 case SPEED_KTS: // kts
164 ret = kts_speed;
165 break;
166 case SPEED_MPH: // mph
167 ret = kts_speed * 1.15078;
168 break;
169 case SPEED_KMH: // km/h
170 ret = kts_speed * 1.852;
171 break;
172 case SPEED_MS: // m/s
173 ret = kts_speed * 0.514444444;
174 break;
175 }
176 return ret;
177}
178
179/**************************************************************************/
180/* Converts the wind speed to the units selected by user */
181/**************************************************************************/
182double toUsrWindSpeed(double kts_wspeed, int unit) {
183 double ret = NAN;
184 if (unit == -1) unit = g_iWindSpeedFormat;
185 switch (unit) {
186 case WSPEED_KTS: // kts
187 ret = kts_wspeed;
188 break;
189 case WSPEED_MS: // m/s
190 ret = kts_wspeed * 0.514444444;
191 break;
192 case WSPEED_MPH: // mph
193 ret = kts_wspeed * 1.15078;
194 break;
195 case WSPEED_KMH: // km/h
196 ret = kts_wspeed * 1.852;
197 break;
198 }
199 return ret;
200}
201
202/**************************************************************************/
203/* Converts the distance to the units selected by user */
204/**************************************************************************/
205double toUsrDistance(double nm_distance, int unit) {
206 double ret = NAN;
207 if (unit == -1) unit = g_iDistanceFormat;
208 switch (unit) {
209 case DISTANCE_NMI: // Nautical miles
210 ret = nm_distance;
211 break;
212 case DISTANCE_MI: // Statute miles
213 ret = nm_distance * 1.15078;
214 break;
215 case DISTANCE_KM:
216 ret = nm_distance * 1.852;
217 break;
218 case DISTANCE_M:
219 ret = nm_distance * 1852;
220 break;
221 case DISTANCE_FT:
222 ret = nm_distance * 6076.12;
223 break;
224 case DISTANCE_FA:
225 ret = nm_distance * 1012.68591;
226 break;
227 case DISTANCE_IN:
228 ret = nm_distance * 72913.4;
229 break;
230 case DISTANCE_CM:
231 ret = nm_distance * 185200;
232 break;
233 }
234 return ret;
235}
236
237/**************************************************************************/
238/* Converts the temperature to the units selected by user */
239/**************************************************************************/
240double toUsrTemp(double cel_temp, int unit) {
241 double ret = NAN;
242 if (unit == -1) unit = g_iTempFormat;
243 switch (unit) {
244 case TEMPERATURE_C: // Celsius
245 ret = cel_temp;
246 break;
247 case TEMPERATURE_F: // Fahrenheit
248 ret = (cel_temp * 9.0 / 5.0) + 32;
249 break;
250 case TEMPERATURE_K:
251 ret = cel_temp + 273.15;
252 break;
253 }
254 return ret;
255}
256
257/**************************************************************************/
258/* Returns the abbreviation of user selected temperature unit */
259/**************************************************************************/
260wxString getUsrTempUnit(int unit) {
261 wxString ret;
262 if (unit == -1) unit = g_iTempFormat;
263 switch (unit) {
264 case TEMPERATURE_C: // Celsius
265 ret = _("C");
266 break;
267 case TEMPERATURE_F: // Fahrenheit
268 ret = _("F");
269 break;
270 case TEMPERATURE_K: // Kelvin
271 ret = _("K");
272 break;
273 }
274 return ret;
275}
276
277/**************************************************************************/
278/* Returns the abbreviation of user selected distance unit */
279/**************************************************************************/
280wxString getUsrDistanceUnit(int unit) {
281 wxString ret;
282 if (unit == -1) unit = g_iDistanceFormat;
283 switch (unit) {
284 case DISTANCE_NMI: // Nautical miles
285 ret = _("NMi");
286 break;
287 case DISTANCE_MI: // Statute miles
288 ret = _("mi");
289 break;
290 case DISTANCE_KM:
291 ret = _("km");
292 break;
293 case DISTANCE_M:
294 ret = _("m");
295 break;
296 case DISTANCE_FT:
297 ret = _("ft");
298 break;
299 case DISTANCE_FA:
300 ret = _("fa");
301 break;
302 case DISTANCE_IN:
303 ret = _("in");
304 break;
305 case DISTANCE_CM:
306 ret = _("cm");
307 break;
308 }
309 return ret;
310}
311
312/**************************************************************************/
313/* Returns the abbreviation of user selected speed unit */
314/**************************************************************************/
315wxString getUsrSpeedUnit(int unit) {
316 wxString ret;
317 if (unit == -1) unit = g_iSpeedFormat;
318 switch (unit) {
319 case SPEED_KTS: // kts
320 ret = _("kts");
321 break;
322 case SPEED_MPH: // mph
323 ret = _("mph");
324 break;
325 case SPEED_KMH:
326 ret = _("km/h");
327 break;
328 case SPEED_MS:
329 ret = _("m/s");
330 break;
331 }
332 return ret;
333}
334
335/**************************************************************************/
336/* Returns the abbreviation of user selected wind speed unit */
337/**************************************************************************/
338wxString getUsrWindSpeedUnit(int unit) {
339 wxString ret;
340 if (unit == -1) unit = g_iWindSpeedFormat;
341 switch (unit) {
342 case WSPEED_KTS: // kts
343 ret = _("kts");
344 break;
345 case WSPEED_MS:
346 ret = _("m/s");
347 break;
348 case WSPEED_MPH: // mph
349 ret = _("mph");
350 break;
351 case WSPEED_KMH:
352 ret = _("km/h");
353 break;
354 }
355 return ret;
356}
357
358wxString FormatDistanceAdaptive(double distance) {
359 wxString result;
360 int unit = g_iDistanceFormat;
361 double usrDistance = toUsrDistance(distance, unit);
362 if (usrDistance < 0.1 &&
363 (unit == DISTANCE_KM || unit == DISTANCE_MI || unit == DISTANCE_NMI)) {
364 unit = (unit == DISTANCE_MI) ? DISTANCE_FT : DISTANCE_M;
365 usrDistance = toUsrDistance(distance, unit);
366 }
367 wxString format;
368 if (usrDistance < 5.0) {
369 format = _T("%1.2f ");
370 } else if (usrDistance < 100.0) {
371 format = _T("%2.1f ");
372 } else if (usrDistance < 1000.0) {
373 format = _T("%3.0f ");
374 } else {
375 format = _T("%4.0f ");
376 }
377 result << wxString::Format(format, usrDistance) << getUsrDistanceUnit(unit);
378 return result;
379}
380
381/**************************************************************************/
382/* Converts the speed from the units selected by user to knots */
383/**************************************************************************/
384double fromUsrSpeed(double usr_speed, int unit, int default_val) {
385 double ret = NAN;
386
387 if (unit == -1) unit = default_val;
388 switch (unit) {
389 case SPEED_KTS: // kts
390 ret = usr_speed;
391 break;
392 case SPEED_MPH: // mph
393 ret = usr_speed / 1.15078;
394 break;
395 case SPEED_KMH: // km/h
396 ret = usr_speed / 1.852;
397 break;
398 case SPEED_MS: // m/s
399 ret = usr_speed / 0.514444444;
400 break;
401 }
402 return ret;
403}
404
405/**************************************************************************/
406/* Converts the distance from the units selected by user to NMi */
407/**************************************************************************/
408double fromUsrDistance(double usr_distance, int unit, int default_val) {
409 double ret = NAN;
410 if (unit == -1) unit = default_val;
411 switch (unit) {
412 case DISTANCE_NMI: // Nautical miles
413 ret = usr_distance;
414 break;
415 case DISTANCE_MI: // Statute miles
416 ret = usr_distance / 1.15078;
417 break;
418 case DISTANCE_KM:
419 ret = usr_distance / 1.852;
420 break;
421 case DISTANCE_M:
422 ret = usr_distance / 1852;
423 break;
424 case DISTANCE_FT:
425 ret = usr_distance / 6076.12;
426 break;
427 }
428 return ret;
429}
430
431/**************************************************************************/
432/* Converts the depth in meters to the units selected by user */
433/**************************************************************************/
434double toUsrDepth(double cel_depth, int unit) {
435 double ret = NAN;
436 if (unit == -1) unit = g_nDepthUnitDisplay;
437 switch (unit) {
438 case DEPTH_FT: // Feet
439 ret = cel_depth / 0.3048;
440 break;
441 case DEPTH_M: // Meters
442 ret = cel_depth;
443 break;
444 case DEPTH_FA:
445 ret = cel_depth / 0.3048 / 6;
446 break;
447 }
448 return ret;
449}
450
451/**************************************************************************/
452/* Converts the depth from the units selected by user to Meters */
453/**************************************************************************/
454double fromUsrDepth(double usr_depth, int unit) {
455 double ret = NAN;
456 if (unit == -1) unit = g_nDepthUnitDisplay;
457 switch (unit) {
458 case DEPTH_FT: // Feet
459 ret = usr_depth * 0.3048;
460 break;
461 case DEPTH_M: // Feet
462 ret = usr_depth;
463 break;
464 case DEPTH_FA: // Fathoms
465 ret = usr_depth * 0.3048 * 6;
466 break;
467 }
468 return ret;
469}
470
471/**************************************************************************/
472/* Returns the abbreviation of user selected depth unit */
473/**************************************************************************/
474wxString getUsrDepthUnit(int unit) {
475 wxString ret;
476 if (unit == -1) unit = g_nDepthUnitDisplay;
477 switch (unit) {
478 case DEPTH_FT: // Feet
479 ret = _("ft");
480 break;
481 case DEPTH_M: // Meters
482 ret = _("m");
483 break;
484 case DEPTH_FA: // Fathoms
485 ret = _("fa");
486 break;
487 }
488 return ret;
489}
490
491//---------------------------------------------------------------------------------
492// Vector Stuff for Hit Test Algorithm
493//---------------------------------------------------------------------------------
494double vGetLengthOfNormal(pVector2D a, pVector2D b, pVector2D n) {
495 vector2D c, vNormal;
496 vNormal.x = 0;
497 vNormal.y = 0;
498 //
499 // Obtain projection vector.
500 //
501 // c = ((a * b)/(|b|^2))*b
502 //
503 c.x = b->x * (vDotProduct(a, b) / vDotProduct(b, b));
504 c.y = b->y * (vDotProduct(a, b) / vDotProduct(b, b));
505 //
506 // Obtain perpendicular projection : e = a - c
507 //
508 vSubtractVectors(a, &c, &vNormal);
509 //
510 // Fill PROJECTION structure with appropriate values.
511 //
512 *n = vNormal;
513
514 return (vVectorMagnitude(&vNormal));
515}
516
517double vDotProduct(pVector2D v0, pVector2D v1) {
518 double dotprod;
519
520 dotprod =
521 (v0 == NULL || v1 == NULL) ? 0.0 : (v0->x * v1->x) + (v0->y * v1->y);
522
523 return (dotprod);
524}
525
526pVector2D vAddVectors(pVector2D v0, pVector2D v1, pVector2D v) {
527 if (v0 == NULL || v1 == NULL)
528 v = (pVector2D)NULL;
529 else {
530 v->x = v0->x + v1->x;
531 v->y = v0->y + v1->y;
532 }
533 return (v);
534}
535
536pVector2D vSubtractVectors(pVector2D v0, pVector2D v1, pVector2D v) {
537 if (v0 == NULL || v1 == NULL)
538 v = (pVector2D)NULL;
539 else {
540 v->x = v0->x - v1->x;
541 v->y = v0->y - v1->y;
542 }
543 return (v);
544}
545
546double vVectorSquared(pVector2D v0) {
547 double dS;
548
549 if (v0 == NULL)
550 dS = 0.0;
551 else
552 dS = ((v0->x * v0->x) + (v0->y * v0->y));
553 return (dS);
554}
555
556double vVectorMagnitude(pVector2D v0) {
557 double dMagnitude;
558
559 if (v0 == NULL)
560 dMagnitude = 0.0;
561 else
562 dMagnitude = sqrt(vVectorSquared(v0));
563 return (dMagnitude);
564}
565
566// This function parses a string containing a GPX time representation
567// and returns a wxDateTime containing the UTC corresponding to the
568// input. The function return value is a pointer past the last valid
569// character parsed (if successful) or NULL (if the string is invalid).
570//
571// Valid GPX time strings are in ISO 8601 format as follows:
572//
573// [-]<YYYY>-<MM>-<DD>T<hh>:<mm>:<ss>Z|(+|-<hh>:<mm>)
574//
575// For example, 2010-10-30T14:34:56Z and 2010-10-30T14:34:56-04:00
576// are the same time. The first is UTC and the second is EDT.
577
578const wxChar *ParseGPXDateTime(wxDateTime &dt, const wxChar *datetime) {
579 long sign, hrs_west, mins_west;
580 const wxChar *end;
581
582 // Skip any leading whitespace
583 while (isspace(*datetime)) datetime++;
584
585 // Skip (and ignore) leading hyphen
586 if (*datetime == wxT('-')) datetime++;
587
588 // Parse and validate ISO 8601 date/time string
589 if ((end = dt.ParseFormat(datetime, wxT("%Y-%m-%dT%T"))) != NULL) {
590 // Invalid date/time
591 if (*end == 0) return NULL;
592
593 // ParseFormat outputs in UTC if the controlling
594 // wxDateTime class instance has not been initialized.
595
596 // Date/time followed by UTC time zone flag, so we are done
597 else if (*end == wxT('Z')) {
598 end++;
599 return end;
600 }
601
602 // Date/time followed by given number of hrs/mins west of UTC
603 else if (*end == wxT('+') || *end == wxT('-')) {
604 // Save direction from UTC
605 if (*end == wxT('+'))
606 sign = 1;
607 else
608 sign = -1;
609 end++;
610
611 // Parse hrs west of UTC
612 if (isdigit(*end) && isdigit(*(end + 1)) && *(end + 2) == wxT(':')) {
613 // Extract and validate hrs west of UTC
614 wxString(end).ToLong(&hrs_west);
615 if (hrs_west > 12) return NULL;
616 end += 3;
617
618 // Parse mins west of UTC
619 if (isdigit(*end) && isdigit(*(end + 1))) {
620 // Extract and validate mins west of UTC
621 wxChar mins[3];
622 mins[0] = *end;
623 mins[1] = *(end + 1);
624 mins[2] = 0;
625 wxString(mins).ToLong(&mins_west);
626 if (mins_west > 59) return NULL;
627
628 // Apply correction
629 dt -= sign * wxTimeSpan(hrs_west, mins_west, 0, 0);
630 return end + 2;
631 } else
632 // Missing mins digits
633 return NULL;
634 } else
635 // Missing hrs digits or colon
636 return NULL;
637 } else
638 // Unknown field after date/time (not UTC, not hrs/mins
639 // west of UTC)
640 return NULL;
641 } else
642 // Invalid ISO 8601 date/time
643 return NULL;
644}
645
646wxString formatTimeDelta(wxTimeSpan span) {
647 using namespace std;
648 // wxTimeSpan is returns complete span in different units.
649 // FIXME: (leamas) Replace with sane std::chrono.
650 stringstream ss;
651 ss << setfill(' ');
652 if (span.GetHours() > 0) span = RoundToMinutes(span);
653 if (span.GetDays() > 0) ss << setw(2) << span.GetDays() << "d ";
654 if (span.GetHours() > 0) {
655 ss << setw(2) << span.GetHours() % 24 << _("H ");
656 ss << setw(2) << span.GetMinutes() % 60 << _("M");
657 } else {
658 ss << setw(2) << span.GetMinutes() % 60 << _("M ");
659 ss << setw(2) << span.GetSeconds() % 60 << _("S");
660 }
661 return ss.str();
662}
663
664wxString formatTimeDelta(wxDateTime startTime, wxDateTime endTime) {
665 wxString timeStr;
666 if (startTime.IsValid() && endTime.IsValid()) {
667 wxTimeSpan span = endTime - startTime;
668 return formatTimeDelta(span);
669 } else {
670 return _("N/A");
671 }
672}
673
674wxString formatTimeDelta(wxLongLong secs) {
675 wxString timeStr;
676
677 wxTimeSpan span(0, 0, secs);
678 return formatTimeDelta(span);
679}
680
681// RFC4122 version 4 compliant random UUIDs generator.
682wxString GpxDocument::GetUUID(void) {
683 wxString str;
684 struct {
685 int time_low;
686 int time_mid;
687 int time_hi_and_version;
688 int clock_seq_hi_and_rsv;
689 int clock_seq_low;
690 int node_hi;
691 int node_low;
692 } uuid;
693
694 uuid.time_low = GetRandomNumber(
695 0, 2147483647); // FIXME: the max should be set to something like
696 // MAXINT32, but it doesn't compile un gcc...
697 uuid.time_mid = GetRandomNumber(0, 65535);
698 uuid.time_hi_and_version = GetRandomNumber(0, 65535);
699 uuid.clock_seq_hi_and_rsv = GetRandomNumber(0, 255);
700 uuid.clock_seq_low = GetRandomNumber(0, 255);
701 uuid.node_hi = GetRandomNumber(0, 65535);
702 uuid.node_low = GetRandomNumber(0, 2147483647);
703
704 /* Set the two most significant bits (bits 6 and 7) of the
705 * clock_seq_hi_and_rsv to zero and one, respectively. */
706 uuid.clock_seq_hi_and_rsv = (uuid.clock_seq_hi_and_rsv & 0x3F) | 0x80;
707
708 /* Set the four most significant bits (bits 12 through 15) of the
709 * time_hi_and_version field to 4 */
710 uuid.time_hi_and_version = (uuid.time_hi_and_version & 0x0fff) | 0x4000;
711
712 str.Printf(_T("%08x-%04x-%04x-%02x%02x-%04x%08x"), uuid.time_low,
713 uuid.time_mid, uuid.time_hi_and_version, uuid.clock_seq_hi_and_rsv,
714 uuid.clock_seq_low, uuid.node_hi, uuid.node_low);
715
716 return str;
717}
718
719int GpxDocument::GetRandomNumber(int range_min, int range_max) {
720 long u = (long)wxRound(
721 ((double)rand() / ((double)(RAND_MAX) + 1) * (range_max - range_min)) +
722 range_min);
723 return (int)u;
724}
725
726/****************************************************************************/
727// Modified from the code posted by Andy Ross at
728// http://www.mail-archive.com/flightgear-devel@flightgear.org/msg06702.html
729// Basically, it looks for a list of decimal numbers embedded in the
730// string and uses the first three as degree, minutes and seconds. The
731// presence of a "S" or "W character indicates that the result is in a
732// hemisphere where the final answer must be negated. Non-number
733// characters are treated as whitespace separating numbers.
734//
735// So there are lots of bogus strings you can feed it to get a bogus
736// answer, but that's not surprising. It does, however, correctly parse
737// all the well-formed strings I can thing of to feed it. I've tried all
738// the following:
739//
740// 37°54.204' N
741// N37 54 12
742// 37°54'12"
743// 37.9034
744// 122°18.621' W
745// 122w 18 37
746// -122.31035
747/****************************************************************************/
748double fromDMM(wxString sdms) {
749 wchar_t buf[64];
750 char narrowbuf[64];
751 int i, len, top = 0;
752 double stk[32], sign = 1;
753
754 // First round of string modifications to accomodate some known strange
755 // formats
756 wxString replhelper;
757 replhelper = wxString::FromUTF8("´·"); // UKHO PDFs
758 sdms.Replace(replhelper, _T("."));
759 replhelper =
760 wxString::FromUTF8("\"·"); // Don't know if used, but to make sure
761 sdms.Replace(replhelper, _T("."));
762 replhelper = wxString::FromUTF8("·");
763 sdms.Replace(replhelper, _T("."));
764
765 replhelper =
766 wxString::FromUTF8("s. š."); // Another example: cs.wikipedia.org
767 // (someone was too active translating...)
768 sdms.Replace(replhelper, _T("N"));
769 replhelper = wxString::FromUTF8("j. š.");
770 sdms.Replace(replhelper, _T("S"));
771 sdms.Replace(_T("v. d."), _T("E"));
772 sdms.Replace(_T("z. d."), _T("W"));
773
774 // If the string contains hemisphere specified by a letter, then '-' is for
775 // sure a separator...
776 sdms.UpperCase();
777 if (sdms.Contains(_T("N")) || sdms.Contains(_T("S")) ||
778 sdms.Contains(_T("E")) || sdms.Contains(_T("W")))
779 sdms.Replace(_T("-"), _T(" "));
780
781 wcsncpy(buf, sdms.wc_str(wxConvUTF8), 63);
782 buf[63] = 0;
783 len = wxMin(wcslen(buf), sizeof(narrowbuf) - 1);
784 ;
785
786 for (i = 0; i < len; i++) {
787 wchar_t c = buf[i];
788 if ((c >= '0' && c <= '9') || c == '-' || c == '.' || c == '+') {
789 narrowbuf[i] = c;
790 continue; /* Digit characters are cool as is */
791 }
792 if (c == ',') {
793 narrowbuf[i] = '.'; /* convert to decimal dot */
794 continue;
795 }
796 if ((c | 32) == 'w' || (c | 32) == 's')
797 sign = -1; /* These mean "negate" (note case insensitivity) */
798 narrowbuf[i] = 0; /* Replace everything else with nuls */
799 }
800
801 /* Build a stack of doubles */
802 stk[0] = stk[1] = stk[2] = 0;
803 for (i = 0; i < len; i++) {
804 while (i < len && narrowbuf[i] == 0) i++;
805 if (i != len) {
806 stk[top++] = atof(narrowbuf + i);
807 i += strlen(narrowbuf + i);
808 }
809 }
810
811 return sign * (stk[0] + (stk[1] + stk[2] / 60) / 60);
812}
813
814double toMagnetic(double deg_true) {
815 if (!std::isnan(gVar)) {
816 if ((deg_true - gVar) > 360.)
817 return (deg_true - gVar - 360.);
818 else
819 return ((deg_true - gVar) >= 0.) ? (deg_true - gVar)
820 : (deg_true - gVar + 360.);
821 } else {
822 if ((deg_true - g_UserVar) > 360.)
823 return (deg_true - g_UserVar - 360.);
824 else
825 return ((deg_true - g_UserVar) >= 0.) ? (deg_true - g_UserVar)
826 : (deg_true - g_UserVar + 360.);
827 }
828}
829
830double toMagnetic(double deg_true, double variation) {
831 double degm = deg_true - variation;
832 if (degm >= 360.)
833 return degm - 360.;
834 else
835 return degm >= 0. ? degm : degm + 360.;
836}