]> git.itanic.dy.fi Git - maemo-mapper/blob - src/util.c
Corrected conversion to British National Grid
[maemo-mapper] / src / util.c
1 /*
2  * Copyright (C) 2006, 2007 John Costigan.
3  *
4  * POI and GPS-Info code originally written by Cezary Jackiewicz.
5  *
6  * Default map data provided by http://www.openstreetmap.org/
7  *
8  * This file is part of Maemo Mapper.
9  *
10  * Maemo Mapper 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 3 of the License, or
13  * (at your option) any later version.
14  *
15  * Maemo Mapper 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 Maemo Mapper.  If not, see <http://www.gnu.org/licenses/>.
22  */
23
24 #ifdef HAVE_CONFIG_H
25 #    include "config.h"
26 #endif
27
28 #define _GNU_SOURCE
29
30 #include <ctype.h>
31 #include <string.h>
32 #include <math.h>
33
34 #ifndef LEGACY
35 #    include <hildon/hildon-note.h>
36 #else
37 #    include <hildon-widgets/hildon-note.h>
38 #endif
39
40 #include "types.h"
41 #include "data.h"
42 #include "defines.h"
43
44 #include "gpx.h"
45 #include "util.h"
46
47
48 /**
49  * Pop up a modal dialog box with simple error information in it.
50  */
51 void
52 popup_error(GtkWidget *window, const gchar *error)
53 {
54     GtkWidget *dialog;
55     printf("%s(\"%s\")\n", __PRETTY_FUNCTION__, error);
56
57     dialog = hildon_note_new_information(GTK_WINDOW(window), error);
58
59     gtk_dialog_run(GTK_DIALOG(dialog));
60     gtk_widget_destroy(dialog);
61
62     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
63 }
64
65 void
66 deg_format(gdouble coor, gchar *scoor, gchar neg_char, gchar pos_char)
67 {
68     gdouble min;
69     gdouble acoor = fabs(coor);
70     printf("%s()\n", __PRETTY_FUNCTION__);
71
72     switch(_degformat)
73     {
74             case IARU_LOC:
75         case UK_OSGB:
76         case UK_NGR:
77                 // These formats should not be formatted in the same way
78                 // - they need to be converted first, therefore if we reach 
79                 // this bit of code use the first available format - drop through.
80         case DDPDDDDD:
81             sprintf(scoor, "%.5f°", coor);
82             break;
83
84         case DDPDDDDD_NSEW:
85             sprintf(scoor, "%.5f° %c", acoor,
86                     coor < 0.0 ? neg_char : pos_char);
87             break;
88         case NSEW_DDPDDDDD:
89             sprintf(scoor, "%c %.5f°",
90                     coor < 0.0 ? neg_char : pos_char,
91                     acoor);
92             break;
93
94         case DD_MMPMMM:
95             sprintf(scoor, "%d°%06.3f'",
96                     (int)coor, (acoor - (int)acoor)*60.0);
97             break;
98         case DD_MMPMMM_NSEW:
99             sprintf(scoor, "%d°%06.3f' %c",
100                     (int)acoor, (acoor - (int)acoor)*60.0,
101                     coor < 0.0 ? neg_char : pos_char);
102             break;
103         case NSEW_DD_MMPMMM:
104             sprintf(scoor, "%c %d° %06.3f'",
105                     coor < 0.0 ? neg_char : pos_char,
106                     (int)acoor, (acoor - (int)acoor)*60.0);
107             break;
108
109         case DD_MM_SSPS:
110             min = (acoor - (int)acoor)*60.0;
111             sprintf(scoor, "%d°%02d'%04.1f\"", (int)coor, (int)min,
112                     ((min - (int)min)*60.0));
113             break;
114         case DD_MM_SSPS_NSEW:
115             min = (acoor - (int)acoor)*60.0;
116             sprintf(scoor, "%d°%02d'%04.1f\" %c", (int)acoor, (int)min,
117                     ((min - (int)min)*60.0),
118                     coor < 0.0 ? neg_char : pos_char);
119             break;
120         case NSEW_DD_MM_SSPS:
121             min = (acoor - (int)acoor)*60.0;
122             sprintf(scoor, "%c %d° %02d' %04.1f\"",
123                     coor < 0.0 ? neg_char : pos_char,
124                     (int)acoor, (int)min,
125                     ((min - (int)min)*60.0));
126             break;
127     }
128     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
129 }
130
131 /** Return the location (in units) of the given string address.  This function
132   * makes a call to the internet, so it may take some time. */
133 Point locate_address(GtkWidget *parent, const gchar *addr)
134 {
135     Path temp;
136     Point retval = _point_null;
137     GnomeVFSResult vfs_result;
138     gint size;
139     gchar *bytes = NULL;
140     gchar *addr_escaped;
141     gchar *buffer;
142     printf("%s(%s)\n", __PRETTY_FUNCTION__, addr);
143
144     addr_escaped = gnome_vfs_escape_string(addr);
145     buffer = g_strdup_printf(_route_dl_url, addr_escaped, addr_escaped);
146     g_free(addr_escaped);
147
148     /* Attempt to download the route from the server. */
149     if(GNOME_VFS_OK != (vfs_result = gnome_vfs_read_entire_file(
150                 buffer, &size, &bytes)))
151     {
152         g_free(buffer);
153         g_free(bytes);
154         popup_error(parent, _("Failed to connect to GPX Directions server"));
155         return _point_null;
156     }
157
158     g_free(buffer);
159
160     MACRO_PATH_INIT(temp);
161
162     if(strncmp(bytes, "<?xml", strlen("<?xml")))
163     {
164         /* Not an XML document - must be bad locations. */
165         popup_error(parent, _("Invalid address."));
166     }
167     /* Else, if GPS is enabled, replace the route, otherwise append it. */
168     else if(!gpx_path_parse(&temp, bytes, size, 0) || !temp.head[1].unity)
169     {
170         popup_error(parent, _("Unknown error while locating address."));
171     }
172     else
173     {
174         /* Save Destination in Route Locations list. */
175         GtkTreeIter iter;
176         if(!g_slist_find_custom(_loc_list, addr, (GCompareFunc)strcmp))
177         {
178             _loc_list = g_slist_prepend(_loc_list, g_strdup(addr));
179             gtk_list_store_insert_with_values(_loc_model, &iter,
180                     INT_MAX, 0, addr, -1);
181         }
182
183         retval = temp.head[1];
184     }
185
186     MACRO_PATH_FREE(temp);
187     g_free(bytes);
188
189     vprintf("%s(): return (%d, %d)\n", __PRETTY_FUNCTION__,
190             retval.unitx, retval.unity);
191     return retval;
192 }
193
194 /**
195  * Calculate the distance between two lat/lon pairs.  The distance is returned
196  * in nautical miles and should be converted using UNITS_CONVERT[_units].
197  */
198 gdouble
199 calculate_distance(gdouble lat1, gdouble lon1, gdouble lat2, gdouble lon2)
200 {
201     gdouble dlat, dlon, slat, slon, a;
202
203     /* Convert to radians. */
204     lat1 = deg2rad(lat1);
205     lon1 = deg2rad(lon1);
206     lat2 = deg2rad(lat2);
207     lon2 = deg2rad(lon2);
208
209     dlat = lat2 - lat1;
210     dlon = lon2 - lon1;
211
212     slat = sin(dlat / 2.0);
213     slon = sin(dlon / 2.0);
214     a = (slat * slat) + (cos(lat1) * cos(lat2) * slon * slon);
215
216     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
217     return ((2.0 * atan2(sqrt(a), sqrt(1.0 - a))) * EARTH_RADIUS);
218 }
219
220 /**
221  * Calculate the bearing between two lat/lon pairs.  The bearing is returned
222  * as the angle from lat1/lon1 to lat2/lon2.
223  */
224 gdouble
225 calculate_bearing(gdouble lat1, gdouble lon1, gdouble lat2, gdouble lon2)
226 {
227     gdouble x, y;
228     gdouble dlon = deg2rad(lon2 - lon1);
229     lat1 = deg2rad(lat1);
230     lat2 = deg2rad(lat2);
231
232     y = sin(dlon) * cos(lat2);
233     x = (cos(lat1) * sin(lat2)) - (sin(lat1) * cos(lat2) * cos(dlon));
234
235     dlon = rad2deg(atan2(y, x));
236     if(dlon < 0.0)
237         dlon += 360.0;
238     return dlon;
239 }
240
241
242
243 void
244 force_min_visible_bars(HildonControlbar *control_bar, gint num_bars)
245 {
246 #ifdef LEGACY
247     GValue val;
248     printf("%s()\n", __PRETTY_FUNCTION__);
249     memset(&val, 0, sizeof(val));
250     g_value_init(&val, G_TYPE_INT);
251     g_value_set_int(&val, num_bars);
252     g_object_set_property(G_OBJECT(control_bar), "minimum-visible-bars", &val);
253     vprintf("%s(): return\n", __PRETTY_FUNCTION__);
254 #endif
255 }
256
257 gboolean
258 banner_reset()
259 {
260     printf("%s()\n", __PRETTY_FUNCTION__);
261
262     /* Re-enable any banners that might have been up. */
263     {
264         if(_connect_banner)
265         {
266             gtk_widget_hide(_connect_banner);
267             gtk_widget_unrealize(_connect_banner);
268             gtk_widget_realize(_connect_banner);
269             gtk_widget_show(_connect_banner);
270         }
271
272         if(_fix_banner)
273         {
274             gtk_widget_hide(_fix_banner);
275             gtk_widget_unrealize(_fix_banner);
276             gtk_widget_realize(_fix_banner);
277             gtk_widget_show(_fix_banner);
278         }
279
280         if(_waypoint_banner)
281         {
282             gtk_widget_hide(_waypoint_banner);
283             gtk_widget_unrealize(_waypoint_banner);
284             gtk_widget_realize(_waypoint_banner);
285             gtk_widget_show(_waypoint_banner);
286         }
287
288         if(_download_banner)
289         {
290             gtk_widget_hide(_download_banner);
291             gtk_widget_unrealize(_download_banner);
292             gtk_widget_realize(_download_banner);
293             gtk_widget_show(_download_banner);
294         }
295
296         /*
297         ConnState old_state = _gps_state;
298         set_conn_state(RCVR_OFF);
299         set_conn_state(old_state);
300         */
301     }
302
303     vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
304     return FALSE;
305 }
306 /*
307  * Get one numeric token off the string, fractional part separator
308  * is ',' or '.' and number may follow any token from sep.
309  * Return value found is in d.
310  *
311  * If utf8_deg is set then accept also Unicode degree symbol U+00B0
312  * encoded as UTF-8 octets 0xc2 0xb0.
313  *
314  * @returns
315  *    0 : on error.
316  *    1 : when nothing or just white space was seen.
317  *    2 : when fractional number was seen.
318  *    3 : when whole number was seen.
319  *  In case 0, endptr points to the beginning of the input string nptr,
320  *  d is undefined.
321  *
322  *  In case 1 endptr points past the whitespace, d is undefined.
323  *
324  *  In cases 2 and 3 the found number is stored in d and endptr points
325  *  to first char not part of the number.
326  *
327  */
328 static gint
329 strdmstod_1(gdouble *d, gchar *nptr, gchar **endptr, gchar *sep, gint utf8_deg)
330 {
331     guchar *p;
332     gint v_flag, f_flag;
333     gdouble v;
334
335     p = nptr;
336
337     while (*p && isspace(*p)) {
338         p++;
339     }
340
341     v_flag = 0;
342     f_flag = 0;
343
344     /* whole part */
345     v = 0.0;
346     while (*p && isdigit(*p)) {
347         v *= 10;
348         v += *p++ - '0';
349         v_flag = 1;
350     }
351
352     if (v_flag) {
353         if (*p && (*p == '.' || *p == ',')) {
354             gdouble f;
355             gint n;
356
357             p++;
358
359             /* fractional part */
360             f = 0.0;
361             n = 1;
362             while (*p && isdigit(*p)) {
363                 f *= 10.0;
364                 f += *p++ - '0';
365                 n *= 10;
366             }
367
368             if (n > 1) {
369                 f_flag = 1;
370                 v += f/n;
371             }
372         }
373
374         /* allow for extra sep char at the end */
375         if (*p && strchr(sep, *p)) {
376             p++;
377         } else if (utf8_deg && *p == 0xc2 && *(p+1) == 0xb0) {
378             p += 2;
379         }
380
381         *d = v;
382         if (endptr) *endptr = p;
383         return f_flag ? 2 : 3;
384     }
385
386     if (endptr) *endptr = p;
387     return *p == 0;
388 }
389
390 static gdouble
391 strdmstod_2(gchar *nptr, gchar **endptr)
392 {
393     gint ret;
394     guchar *p;
395     gdouble d, m, s;
396
397     p = nptr;
398
399     /* degrees */
400     ret = strdmstod_1(&d, p, endptr, "dD@", 1);
401     switch (ret) {
402         case 0: return 0.0;
403         case 1:
404                 if (endptr) *endptr = (char *)nptr;
405                 return 0.0;
406         case 2: return d;
407     }
408
409     /* minutes */
410     p = *endptr;
411     m = 0.0;
412     ret = strdmstod_1(&m, p, endptr, "mM'", 0);
413     switch (ret) {
414         case 0: return 0.0;
415         case 1: return d;
416         case 2: return d + m/60.0;
417     }
418
419     /* seconds */
420     p = *endptr;
421     ret = strdmstod_1(&s, p, endptr, "sS\"", 0);
422     switch (ret) {
423         case 0: return 0.0;
424         case 1: return d + m/60.0;
425         case 2:
426         case 3: return d + m/60.0 + s/3600.0;
427     }
428
429     /* can't be here */
430     return 0.0;
431 }
432
433 /*
434  * format: / \s* [+-NSWE]? \s* ( d | D \s+ m | D \s+ M \s+ s ) [NSWE]? /x
435  * where D := / \d+[@d°]? /ix
436  *       M := / \d+['m]? /ix
437  *       d := / D | \d+[,.]\d+[@d°]? /ix
438  *       m := / M | \d+[,.]\d+['m]? /ix
439  *       s := / S | \d+[,.]\d+["s]? /ix
440  *
441  *   and N or W are treated as positive, S or E are treated as negative,
442  *   they may occur only once.
443  */
444 gdouble
445 strdmstod(const gchar *nptr, gchar **endptr)
446 {
447     gint s;
448     gchar *p, *end;
449     gchar *sign = 0;
450     gdouble d;
451
452     p = (char *)nptr;
453
454     while(*p && isspace(*p))
455         p++;
456
457     if(!*p) {
458         if(endptr)
459             *endptr = (char *)nptr;
460         return 0.0;
461     }
462
463     if(strchr("nwseNWSE-+", *p)) {
464         sign = p;
465         p++;
466     }
467
468     d = strdmstod_2(p, &end);
469     if(p == end && d == 0.0) {
470         if(endptr) *endptr = end;
471         return d;
472     }
473
474     p = end;
475     while(*p && isspace(*p))
476         p++;
477
478     s = 1;
479     if(sign == 0) {
480         if(*p && strchr("nwseNWSE", *p)) {
481             if(tolower(*p) == 's' || tolower(*p) == 'w')
482                 s = -1;
483             p++;
484         }
485     } else {
486         if(tolower(*sign) == 's' || tolower(*sign) == 'w' || *sign == '-')
487             s = -1;
488         printf("s: %d\n", s);
489     }
490
491     if(endptr) *endptr = p;
492     return s * d;
493 }
494
495 double marc(double bf0, double n, double phi0, double phi)
496 {
497         return bf0 * (((1 + n + ((5 / 4) * (n * n)) + ((5 / 4) * (n * n * n))) * (phi - phi0))
498             - (((3 * n) + (3 * (n * n)) + ((21 / 8) * (n * n * n))) * (sin(phi - phi0)) * (cos(phi + phi0)))
499             + ((((15 / 8) * (n * n)) + ((15 / 8) * (n * n * n))) * (sin(2 * (phi - phi0))) * (cos(2 * (phi + phi0))))
500             - (((35 / 24) * (n * n * n)) * (sin(3 * (phi - phi0))) * (cos(3 * (phi + phi0)))));
501 }
502
503 gboolean os_grid_check_lat_lon(double lat, double lon)
504 {
505         // TODO - Check exact OS Grid range
506         if(lat < 50.0 || lat > 62 || lon < -7.5 || lon > 2.2 )
507         {
508                 return FALSE;
509         }
510         else
511         {
512                 return TRUE;
513         }
514 }
515
516 gboolean coord_system_check_lat_lon (gdouble lat, gdouble lon, gint *fallback_deg_format)
517 {
518         // Is the current coordinate system applicable to the provided lat and lon?
519         gboolean valid = FALSE;
520         
521         switch(_degformat)
522         {
523         case UK_OSGB:
524         case UK_NGR:
525                 valid = os_grid_check_lat_lon(lat, lon);
526                 if(fallback_deg_format != NULL) *fallback_deg_format = DDPDDDDD; 
527                 break;
528         default:
529                 valid = TRUE;
530                 break;
531         }
532         
533         return valid;
534 }
535
536 gboolean convert_lat_long_to_os_grid(double lat, double lon, int *easting, int *northing)
537 {
538         if(!os_grid_check_lat_lon(lat, lon))
539         {
540                 return FALSE;
541         }
542         
543         const double deg2rad = (2 * PI / 360);
544         
545         const double phi = lat * deg2rad;          // convert latitude to radians
546         const double lam = lon * deg2rad;          // convert longitude to radians
547         const double a = 6377563.396;              // OSGB semi-major axis
548         const double b = 6356256.91;               // OSGB semi-minor axis
549         const double e0 = 400000;                  // easting of false origin
550         const double n0 = -100000;                 // northing of false origin
551         const double f0 = 0.9996012717;            // OSGB scale factor on central meridian
552         const double e2 = 0.0066705397616;         // OSGB eccentricity squared
553         const double lam0 = -0.034906585039886591; // OSGB false east
554         const double phi0 = 0.85521133347722145;   // OSGB false north
555         const double af0 = a * f0;
556         const double bf0 = b * f0;
557
558         // easting
559         double slat2 = sin(phi) * sin(phi);
560         double nu = af0 / (sqrt(1 - (e2 * (slat2))));
561         double rho = (nu * (1 - e2)) / (1 - (e2 * slat2));
562         double eta2 = (nu / rho) - 1;
563         double p = lam - lam0;
564         double IV = nu * cos(phi);
565         double clat3 = pow(cos(phi), 3);
566         double tlat2 = tan(phi) * tan(phi);
567         double V = (nu / 6) * clat3 * ((nu / rho) - tlat2);
568         double clat5 = pow(cos(phi), 5);
569         double tlat4 = pow(tan(phi), 4);
570         double VI = (nu / 120) * clat5 * ((5 - (18 * tlat2)) + tlat4 + (14 * eta2) - (58 * tlat2 * eta2));
571         double east = e0 + (p * IV) + (pow(p, 3) * V) + (pow(p, 5) * VI);
572                 
573         // northing
574         double n = (af0 - bf0) / (af0 + bf0);
575         double M = marc(bf0, n, phi0, phi);
576         double I = M + (n0);
577         double II = (nu / 2) * sin(phi) * cos(phi);
578         double III = ((nu / 24) * sin(phi) * pow(cos(phi), 3)) * (5 - pow(tan(phi), 2) + (9 * eta2));
579         double IIIA = ((nu / 720) * sin(phi) * clat5) * (61 - (58 * tlat2) + tlat4);
580         double north = I + ((p * p) * II) + (pow(p, 4) * III) + (pow(p, 6) * IIIA);
581
582          // make whole number values
583          *easting = round(east);   // round to whole number
584          *northing = round(north); // round to whole number
585
586          return TRUE;
587 }
588
589 gboolean convert_os_grid_to_bng(gint easting, gint northing, gchar* bng)
590 {
591         gdouble eX = (gdouble)easting / 500000.0;
592         gdouble nX = (gdouble)northing / 500000.0;
593         gdouble tmp = floor(eX) - 5.0 * floor(nX) + 17.0;
594         gchar eing[12];
595         gchar ning[12];
596         
597         nX = 5.0 * (nX - floor(nX));
598         eX = 20.0 - 5.0 * floor(nX) + floor(5.0 * (eX - floor(eX)));
599         if (eX > 7.5) eX = eX + 1;    // I is not used
600         if (tmp > 7.5) tmp = tmp + 1; // I is not used
601         
602         snprintf(eing, 12, "%u", easting);
603         snprintf(ning, 12, "%u", northing);
604         
605         snprintf(eing, 5, "%s", eing+1);
606         snprintf(ning, 5, "%s", ning+1);
607                 
608         sprintf(bng, "%c%c%s%s", 
609                         (char)(tmp + 65), 
610                         (char)(eX + 65),
611                         eing, ning
612                 );
613  
614                 
615         return TRUE;
616 }
617
618 gboolean convert_os_xy_to_latlon(const gchar *easting, const gchar *northing, gdouble *d_lat, gdouble *d_lon)
619 {
620     gint64 i64_n = g_ascii_strtoll (northing, NULL, 10);
621     gint64 i64_e = g_ascii_strtoll (easting, NULL, 10);
622     
623     const double deg2rad = (2 * PI / 360);
624     
625         const gdouble N =  (gdouble)i64_n;
626         const gdouble E = (gdouble)i64_e;
627
628         const gdouble a = 6377563.396, b = 6356256.910;              // Airy 1830 major & minor semi-axes
629         const gdouble F0 = 0.9996012717;                             // NatGrid scale factor on central meridian
630         const gdouble lat0 = 49*PI/180, lon0 = -2*PI/180;  // NatGrid true origin
631         const gdouble N0 = -100000, E0 = 400000;                     // northing & easting of true origin, metres
632         const gdouble e2 = 1 - (b*b)/(a*a);                          // eccentricity squared
633         const gdouble n = (a-b)/(a+b), n2 = n*n, n3 = n*n*n;
634
635         gdouble lat=lat0, M=0;
636         do {
637                 lat = (N-N0-M)/(a*F0) + lat;
638
639                 const gdouble Ma = (1 + n + (5/4)*n2 + (5/4)*n3) * (lat-lat0);
640                 const gdouble Mb = (3*n + 3*n*n + (21/8)*n3) * sin(lat-lat0) * cos(lat+lat0);
641                 const gdouble Mc = ((15/8)*n2 + (15/8)*n3) * sin(2*(lat-lat0)) * cos(2*(lat+lat0));
642                 const gdouble Md = (35/24)*n3 * sin(3*(lat-lat0)) * cos(3*(lat+lat0));
643             M = b * F0 * (Ma - Mb + Mc - Md);                // meridional arc
644
645         } while (N-N0-M >= 0.00001);  // ie until < 0.01mm
646
647         const gdouble cosLat = cos(lat), sinLat = sin(lat);
648         const gdouble nu = a*F0/sqrt(1-e2*sinLat*sinLat);              // transverse radius of curvature
649         const gdouble rho = a*F0*(1-e2)/pow(1-e2*sinLat*sinLat, 1.5);  // meridional radius of curvature
650         const gdouble eta2 = nu/rho-1;
651
652         const gdouble tanLat = tan(lat);
653         const gdouble tan2lat = tanLat*tanLat, tan4lat = tan2lat*tan2lat, tan6lat = tan4lat*tan2lat;
654         const gdouble secLat = 1/cosLat;
655         const gdouble nu3 = nu*nu*nu, nu5 = nu3*nu*nu, nu7 = nu5*nu*nu;
656         const gdouble VII = tanLat/(2*rho*nu);
657         const gdouble VIII = tanLat/(24*rho*nu3)*(5+3*tan2lat+eta2-9*tan2lat*eta2);
658         const gdouble IX = tanLat/(720*rho*nu5)*(61+90*tan2lat+45*tan4lat);
659         const gdouble X = secLat/nu;
660         const gdouble XI = secLat/(6*nu3)*(nu/rho+2*tan2lat);
661         const gdouble XII = secLat/(120*nu5)*(5+28*tan2lat+24*tan4lat);
662         const gdouble XIIA = secLat/(5040*nu7)*(61+662*tan2lat+1320*tan4lat+720*tan6lat);
663
664         const gdouble dE = (E-E0), dE2 = dE*dE, dE3 = dE2*dE, dE4 = dE2*dE2, dE5 = dE3*dE2;
665         const gdouble dE6 = dE4*dE2, dE7 = dE5*dE2;
666         lat = lat - VII*dE2 + VIII*dE4 - IX*dE6;
667         const gdouble lon = lon0 + X*dE - XI*dE3 + XII*dE5 - XIIA*dE7;
668         
669         *d_lon = lon / deg2rad;
670         *d_lat = lat / deg2rad;
671         
672         return TRUE;
673 }
674
675 gboolean convert_os_ngr_to_latlon(const gchar *text, gdouble *d_lat, gdouble *d_lon)
676 {
677         // get numeric values of letter references, mapping A->0, B->1, C->2, etc:
678         gint l1;
679         gint l2;
680
681         gchar s_e[6], s_n[6];
682         gchar easting[7], northing[7];
683         gint64 i64_e = 0;
684         gint64 i64_n = 0;
685         
686         
687         if( ((gchar)text[0])>='a' && ((gchar)text[0]) <= 'z' ) 
688                 l1 = text[0] - (gint)'a'; // lower case
689         else if( ((gchar)text[0])>='A' && ((gchar)text[0]) <= 'Z' )
690                 l1 = text[0] - (gint)'A'; // upper case
691         else
692                 return FALSE; // Not a letter - invalid grid ref
693         
694         if( ((gchar)text[1])>='a' && ((gchar)text[1]) <= 'z' ) 
695                 l2 = text[1] - (gint)'a'; // lower case
696         else if( ((gchar)text[1])>='A' && ((gchar)text[1]) <= 'Z' )
697                 l2 = text[1] - (gint)'A'; // upper case
698         else
699                 return FALSE; // Not a letter - invalid grid ref
700
701         
702         // shuffle down letters after 'I' since 'I' is not used in grid:
703         if (l1 > 7) l1--;
704         if (l2 > 7) l2--;
705
706         // convert grid letters into 100km-square indexes from false origin (grid square SV):
707         gdouble e = ((l1-2)%5)*5 + (l2%5);
708         gdouble n = (19-floor(l1/5)*5) - floor(l2/5);
709
710         // skip grid letters to get numeric part of ref, stripping any spaces:
711         gchar *gridref = (gchar*)(text+2); 
712         
713         // user may have entered a space, so remove any spaces
714         while(gridref[0] == ' ')
715         {
716                 gridref = (gchar*)(text+1);
717         }
718
719         
720         // floor the length incase a space has been added
721         const gint len = (gint)floor((gdouble)strlen(gridref)/2.00); // normally this will be 4, often 3
722         if(len>5) return FALSE; 
723         
724         if(len >0)
725         {
726                 snprintf(s_e, len+1, "%s", gridref);
727                 
728                 while( (gchar)((gint)gridref+len) == ' ' ) 
729                         gridref = (gchar*)(gridref+1); // Allow for a space
730                 
731                 snprintf(s_n, len+1, "%s", gridref+len);
732                 
733                 i64_e = g_ascii_strtoll (s_e, NULL, 10);
734                 i64_n = g_ascii_strtoll (s_n, NULL, 10);
735                 
736                 // Move to most significate values
737                 i64_e *= pow(10, 5-len);
738                 i64_n *= pow(10, 5-len);
739         }
740         
741         // append numeric part of references to grid index:
742         e = (e*100000) + (gdouble)i64_e;
743         n = (n*100000) + (gdouble)i64_n;
744
745         snprintf(easting, 7, "%06u", (gint)e);
746         snprintf(northing, 7, "%06u", (gint)n);
747         
748         convert_os_xy_to_latlon(easting, northing, d_lat, d_lon);
749         
750         return TRUE;
751 }
752
753
754 // Attempt to convert any user entered grid reference to a double lat/lon
755 // return TRUE on valid
756 gboolean parse_coords(const gchar* txt_lat, const gchar* txt_lon, gdouble* lat, gdouble* lon)
757 {
758         gboolean valid = FALSE;
759         
760         // UK_NGR starts with two letters, and then all numbers - it may contain spaces - no lon will be entered
761         if( _degformat == UK_NGR)
762         {
763                 valid = convert_os_ngr_to_latlon(txt_lat, lat, lon);
764
765         if(!valid || *lat < -90. || *lat > 90.) { valid = FALSE; }
766         else   if(*lon < -180. || *lon > 180.)  { valid = FALSE; }
767         }
768         // UK_OSGB contains two 6 digit integers
769         else if( _degformat == UK_OSGB)
770         {
771                 valid = convert_os_xy_to_latlon(txt_lat, txt_lon, lat, lon);
772                 
773         if(!valid || *lat < -90. || *lat > 90.) { valid = FALSE; }
774         else   if(*lon < -180. || *lon > 180.)  { valid = FALSE; }
775
776         }
777         else if( _degformat == IARU_LOC)
778         {
779                 valid = convert_iaru_loc_to_lat_lon(txt_lat, lat, lon);
780
781         if(!valid || *lat < -90. || *lat > 90.) { valid = FALSE; }
782         else   if(*lon < -180. || *lon > 180.)  { valid = FALSE; }
783         }
784         // It must either be invalid, or a lat/lon format
785         else
786         {
787                 gchar* error_check;
788                 *lat = strdmstod(txt_lat, &error_check);
789                 
790         if(txt_lat == error_check || *lat < -90. || *lat > 90.) { valid = FALSE; }
791         else { valid = TRUE; }
792
793         if(valid == TRUE)
794         {
795                 *lon = strdmstod(txt_lon, &error_check);
796                 
797             if(txt_lon == error_check || *lon < -180. || *lon > 180.)  { valid = FALSE; }
798         }
799         }
800         
801         
802         
803         return valid;
804 }
805
806 gboolean convert_iaru_loc_to_lat_lon(const gchar* txt_lon, gdouble* lat, gdouble* lon)
807 {
808         gint u_first = 0;
809         gint u_second = 0;
810         gint u_third = 0;
811         gint u_fourth = 0;
812         gint u_fifth = 0;
813         gint u_sixth = 0;
814         gint u_seventh = 0;
815         gint u_eighth = 0;
816         
817         if(strlen(txt_lon) >= 1)
818         {
819                 if( ((gchar)txt_lon[0])>='a' && ((gchar)txt_lon[0]) <= 'z' ) 
820                         u_first = txt_lon[0] - (gint)'a'; // lower case
821                 else if( ((gchar)txt_lon[0])>='A' && ((gchar)txt_lon[0]) <= 'Z' )
822                         u_first = txt_lon[0] - (gint)'A'; // upper case
823         }
824         
825         if(strlen(txt_lon) >= 2)
826         {
827                 if( ((gchar)txt_lon[1])>='a' && ((gchar)txt_lon[1]) <= 'z' ) 
828                         u_second = txt_lon[1] - (gint)'a'; // lower case
829                 else if( ((gchar)txt_lon[1])>='A' && ((gchar)txt_lon[1]) <= 'Z' )
830                         u_second = txt_lon[1] - (gint)'A'; // upper case
831         }
832         
833         if(strlen(txt_lon) >= 3)
834                 u_third = txt_lon[2] - (gint)'0';
835         
836         if(strlen(txt_lon) >= 4)
837                 u_fourth = txt_lon[3] - (gint)'0';
838
839         if(strlen(txt_lon) >= 5)
840         {
841                 if( ((gchar)txt_lon[4])>='a' && ((gchar)txt_lon[4]) <= 'z' ) 
842                         u_fifth = txt_lon[4] - (gint)'a'; // lower case
843                 else if( ((gchar)txt_lon[4])>='A' && ((gchar)txt_lon[4]) <= 'Z' )
844                         u_fifth = txt_lon[4] - (gint)'A'; // upper case
845         }
846
847         if(strlen(txt_lon) >= 6)
848         {
849                 if( ((gchar)txt_lon[5])>='a' && ((gchar)txt_lon[5]) <= 'z' ) 
850                         u_sixth = txt_lon[5] - (gint)'a'; // lower case
851                 else if( ((gchar)txt_lon[5])>='A' && ((gchar)txt_lon[5]) <= 'Z' )
852                         u_sixth = txt_lon[5] - (gint)'A'; // upper case
853         }
854         
855
856         if(strlen(txt_lon) >= 7)
857                 u_seventh= txt_lon[6] - (gint)'0';
858
859         if(strlen(txt_lon) >= 8)
860                 u_eighth = txt_lon[7] - (gint)'0';
861         
862         *lat = ((gdouble)u_first * 20.0) + ((gdouble)u_third * 2.0) + ((gdouble)u_fifth * (2.0/24.0)) + ((gdouble)u_seventh * (2.0/240.0)) - 90.0;
863         *lon = ((gdouble)u_second * 10.0) + ((gdouble)u_fourth) + ((gdouble)u_sixth * (1.0/24.0)) + ((gdouble)u_eighth * (1.0/240.0)) - 180.0;
864         
865         return TRUE;
866 }
867
868 void convert_lat_lon_to_iaru_loc(gdouble d_lat, gdouble d_lon, gchar *loc)
869 {
870         const gdouble d_a_lat = (d_lat+90.0);
871         const gdouble d_a_lon = (d_lon+180.0);
872                 
873         const gint i_first  = (gint)floor(d_a_lon/20.0);
874         const gint i_second = (gint)floor(d_a_lat/10.0);
875         const gint i_third  = (gint)floor((d_a_lon - (20.0*i_first))/2.0);
876         const gint i_fourth = (gint)floor((d_a_lat - (10.0*i_second)));
877         const gint i_fifth  = (gint)floor((d_a_lon - (20.0*i_first) - (2.0*i_third))/(5.0/60.0));
878         const gint i_sixth  = (gint)floor((d_a_lat - (10.0*i_second) - (i_fourth))/(2.5/60.0));
879         
880         const gint i_seventh  = (gint)floor((d_a_lon - (20.0*i_first) - (2.0*i_third) 
881                         - ((2.0/24.0)*i_fifth))/(2.0/240.0)  );
882         const gint i_eighth = (gint)floor((d_a_lat - (10.0*i_second) - (i_fourth) 
883                         - ((1.0/24.0)*i_sixth))/(1.0/240.0));
884         
885         
886         sprintf(loc, "%c%c%u%u%c%c%u%u",
887                         'A'+i_first,
888                         'A'+i_second,
889                         i_third,
890                         i_fourth,
891                         'a' + i_fifth,
892                         'a' + i_sixth,
893                         i_seventh,
894                         i_eighth);
895 }
896
897 gboolean convert_lat_lon_to_bng(gdouble lat, gdouble lon, gchar* bng)
898 {
899         gint easting, northing;
900
901         if( convert_lat_long_to_os_grid(lat, lon, &easting, &northing) )
902         {
903                 if( convert_os_grid_to_bng(easting, northing, bng) )
904                 {
905                         return TRUE;
906                 }
907         }
908         
909         return FALSE;
910 }
911
912
913 void format_lat_lon(gdouble d_lat, gdouble d_lon, gchar* lat, gchar* lon)
914 {
915         gint east = 0;
916         gint north = 0;
917
918         switch (_degformat)
919         {
920         case UK_OSGB:
921                 
922                 if(convert_lat_long_to_os_grid(d_lat, d_lon, &east, &north))
923                 {
924                         sprintf(lat, "%06d", east);
925                         sprintf(lon, "%06d", north);
926                 }
927                 else
928                 {
929                         // Failed (possibly out of range), so use defaults
930                         lat_format(d_lat, lat);
931                         lon_format(d_lon, lon);                         
932                 }
933                 break;
934         case UK_NGR:
935                 
936                 if(convert_lat_lon_to_bng(d_lat, d_lon, lat))
937                 {
938                         lon[0] = 0;
939                 }
940                 else
941                 {
942                         // Failed (possibly out of range), so use defaults
943                         lat_format(d_lat, lat);
944                         lat_format(d_lon, lon);                         
945                 }
946                 break;
947                 
948         case IARU_LOC:
949                 convert_lat_lon_to_iaru_loc(d_lat, d_lon, lat);
950
951                 break;
952                 
953         default:
954                 lat_format(d_lat, lat);
955                 lon_format(d_lon, lon);
956
957                 break;
958         }
959 }
960
961 #if 0
962 struct t_case {
963     gchar *fmt;
964     gdouble value;
965 } t_cases[] = {
966     { "12°", 12 },
967     { "+12d", 12 },
968     { "-12.345d", -12.345 },
969     { "12.345 E", -12.345 },
970     { "12d34m", 12.5666667 },
971     { "N 12 34", 12.5666667 },
972     { "S 12d34.56m", -12.576 },
973     { "W 12 34.56", 12.576 },
974     { "E 12d34m56s", -12.582222 },
975     { "12 34 56 S", -12.582222 },
976     { "12 34 56", 12.582222 },
977     { "12d34m56.7s E", -12.582417 },
978     { "12 34 56.7 W", 12.582417 },
979     { "12° 34 56.7 W", 12.582417 },
980
981     { 0, 0 }
982 };
983
984 int
985 strdmstod_test()
986 {
987     struct t_case *t;
988     gint fail, ok;
989
990     fail = ok = 0;
991
992     for (t = t_cases; t->fmt; t++) {
993         gdouble v;
994         gchar *endp;
995
996         v = strdmstod(t->fmt, &endp);
997         if (endp == t->fmt || *endp != 0) {
998             fprintf(stderr, "FAIL syntax %s\n", t->fmt);
999             fail++;
1000         } else if (fabs(v - t->value) > 0.000001) {
1001             fprintf(stderr, "FAIL value %s -> %.10g (%.10g)\n",
1002                     t->fmt, v, t->value);
1003             fail++;
1004         } else {
1005             ok++;
1006         }
1007     }
1008
1009     if (fail == 0) {
1010         fprintf(stderr, "ALL TESTS OK\n");
1011     } else {
1012         fprintf(stderr, "FAIL %d, OK %d\n", fail, ok);
1013     }
1014 }
1015 #endif