2 * Copyright (C) 2006, 2007 John Costigan.
4 * POI and GPS-Info code originally written by Cezary Jackiewicz.
6 * Default map data provided by http://www.openstreetmap.org/
8 * This file is part of Maemo Mapper.
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.
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.
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/>.
35 # include <hildon/hildon-note.h>
37 # include <hildon-widgets/hildon-note.h>
47 gboolean convert_iaru_loc_to_lat_lon(const gchar* txt_lon, gdouble* lat, gdouble* lon);
50 * Pop up a modal dialog box with simple error information in it.
53 popup_error(GtkWidget *window, const gchar *error)
56 printf("%s(\"%s\")\n", __PRETTY_FUNCTION__, error);
58 dialog = hildon_note_new_information(GTK_WINDOW(window), error);
60 gtk_dialog_run(GTK_DIALOG(dialog));
61 gtk_widget_destroy(dialog);
63 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
67 deg_format(gdouble coor, gchar *scoor, gchar neg_char, gchar pos_char)
70 gdouble acoor = fabs(coor);
71 printf("%s()\n", __PRETTY_FUNCTION__);
79 // These formats should not be formatted in the same way
80 // - they need to be converted first, therefore if we reach
81 // this bit of code use the first available format - drop through.
83 sprintf(scoor, "%.5f°", coor);
87 sprintf(scoor, "%.5f° %c", acoor,
88 coor < 0.0 ? neg_char : pos_char);
91 sprintf(scoor, "%c %.5f°",
92 coor < 0.0 ? neg_char : pos_char,
97 sprintf(scoor, "%d°%06.3f'",
98 (int)coor, (acoor - (int)acoor)*60.0);
101 sprintf(scoor, "%d°%06.3f' %c",
102 (int)acoor, (acoor - (int)acoor)*60.0,
103 coor < 0.0 ? neg_char : pos_char);
106 sprintf(scoor, "%c %d° %06.3f'",
107 coor < 0.0 ? neg_char : pos_char,
108 (int)acoor, (acoor - (int)acoor)*60.0);
112 min = (acoor - (int)acoor)*60.0;
113 sprintf(scoor, "%d°%02d'%04.1f\"", (int)coor, (int)min,
114 ((min - (int)min)*60.0));
116 case DD_MM_SSPS_NSEW:
117 min = (acoor - (int)acoor)*60.0;
118 sprintf(scoor, "%d°%02d'%04.1f\" %c", (int)acoor, (int)min,
119 ((min - (int)min)*60.0),
120 coor < 0.0 ? neg_char : pos_char);
122 case NSEW_DD_MM_SSPS:
123 min = (acoor - (int)acoor)*60.0;
124 sprintf(scoor, "%c %d° %02d' %04.1f\"",
125 coor < 0.0 ? neg_char : pos_char,
126 (int)acoor, (int)min,
127 ((min - (int)min)*60.0));
130 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
133 /** Return the location (in units) of the given string address. This function
134 * makes a call to the internet, so it may take some time. */
135 Point locate_address(GtkWidget *parent, const gchar *addr)
138 Point retval = _point_null;
139 GnomeVFSResult vfs_result;
144 printf("%s(%s)\n", __PRETTY_FUNCTION__, addr);
146 addr_escaped = gnome_vfs_escape_string(addr);
147 buffer = g_strdup_printf(_route_dl_url, addr_escaped, addr_escaped);
148 g_free(addr_escaped);
150 /* Attempt to download the route from the server. */
151 if(GNOME_VFS_OK != (vfs_result = gnome_vfs_read_entire_file(
152 buffer, &size, &bytes)))
156 popup_error(parent, _("Failed to connect to GPX Directions server"));
162 MACRO_PATH_INIT(temp);
164 if(strncmp(bytes, "<?xml", strlen("<?xml")))
166 /* Not an XML document - must be bad locations. */
167 popup_error(parent, _("Invalid address."));
169 /* Else, if GPS is enabled, replace the route, otherwise append it. */
170 else if(!gpx_path_parse(&temp, bytes, size, 0) || !temp.head[1].unity)
172 popup_error(parent, _("Unknown error while locating address."));
176 /* Save Destination in Route Locations list. */
178 if(!g_slist_find_custom(_loc_list, addr, (GCompareFunc)strcmp))
180 _loc_list = g_slist_prepend(_loc_list, g_strdup(addr));
181 gtk_list_store_insert_with_values(_loc_model, &iter,
182 INT_MAX, 0, addr, -1);
185 retval = temp.head[1];
188 MACRO_PATH_FREE(temp);
191 vprintf("%s(): return (%d, %d)\n", __PRETTY_FUNCTION__,
192 retval.unitx, retval.unity);
197 * Calculate the distance between two lat/lon pairs. The distance is returned
198 * in nautical miles and should be converted using UNITS_CONVERT[_units].
201 calculate_distance(gdouble lat1, gdouble lon1, gdouble lat2, gdouble lon2)
203 gdouble dlat, dlon, slat, slon, a;
205 /* Convert to radians. */
206 lat1 = deg2rad(lat1);
207 lon1 = deg2rad(lon1);
208 lat2 = deg2rad(lat2);
209 lon2 = deg2rad(lon2);
214 slat = sin(dlat / 2.0);
215 slon = sin(dlon / 2.0);
216 a = (slat * slat) + (cos(lat1) * cos(lat2) * slon * slon);
218 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
219 return ((2.0 * atan2(sqrt(a), sqrt(1.0 - a))) * EARTH_RADIUS);
223 * Calculate the bearing between two lat/lon pairs. The bearing is returned
224 * as the angle from lat1/lon1 to lat2/lon2.
227 calculate_bearing(gdouble lat1, gdouble lon1, gdouble lat2, gdouble lon2)
230 gdouble dlon = deg2rad(lon2 - lon1);
231 lat1 = deg2rad(lat1);
232 lat2 = deg2rad(lat2);
234 y = sin(dlon) * cos(lat2);
235 x = (cos(lat1) * sin(lat2)) - (sin(lat1) * cos(lat2) * cos(dlon));
237 dlon = rad2deg(atan2(y, x));
246 force_min_visible_bars(HildonControlbar *control_bar, gint num_bars)
250 printf("%s()\n", __PRETTY_FUNCTION__);
251 memset(&val, 0, sizeof(val));
252 g_value_init(&val, G_TYPE_INT);
253 g_value_set_int(&val, num_bars);
254 g_object_set_property(G_OBJECT(control_bar), "minimum-visible-bars", &val);
255 vprintf("%s(): return\n", __PRETTY_FUNCTION__);
262 printf("%s()\n", __PRETTY_FUNCTION__);
264 /* Re-enable any banners that might have been up. */
268 gtk_widget_hide(_connect_banner);
269 gtk_widget_unrealize(_connect_banner);
270 gtk_widget_realize(_connect_banner);
271 gtk_widget_show(_connect_banner);
276 gtk_widget_hide(_fix_banner);
277 gtk_widget_unrealize(_fix_banner);
278 gtk_widget_realize(_fix_banner);
279 gtk_widget_show(_fix_banner);
284 gtk_widget_hide(_waypoint_banner);
285 gtk_widget_unrealize(_waypoint_banner);
286 gtk_widget_realize(_waypoint_banner);
287 gtk_widget_show(_waypoint_banner);
292 gtk_widget_hide(_download_banner);
293 gtk_widget_unrealize(_download_banner);
294 gtk_widget_realize(_download_banner);
295 gtk_widget_show(_download_banner);
299 ConnState old_state = _gps_state;
300 set_conn_state(RCVR_OFF);
301 set_conn_state(old_state);
305 vprintf("%s(): return FALSE\n", __PRETTY_FUNCTION__);
309 * Get one numeric token off the string, fractional part separator
310 * is ',' or '.' and number may follow any token from sep.
311 * Return value found is in d.
313 * If utf8_deg is set then accept also Unicode degree symbol U+00B0
314 * encoded as UTF-8 octets 0xc2 0xb0.
318 * 1 : when nothing or just white space was seen.
319 * 2 : when fractional number was seen.
320 * 3 : when whole number was seen.
321 * In case 0, endptr points to the beginning of the input string nptr,
324 * In case 1 endptr points past the whitespace, d is undefined.
326 * In cases 2 and 3 the found number is stored in d and endptr points
327 * to first char not part of the number.
331 strdmstod_1(gdouble *d, gchar *nptr, gchar **endptr, gchar *sep, gint utf8_deg)
339 while (*p && isspace(*p)) {
348 while (*p && isdigit(*p)) {
355 if (*p && (*p == '.' || *p == ',')) {
361 /* fractional part */
364 while (*p && isdigit(*p)) {
376 /* allow for extra sep char at the end */
377 if (*p && strchr(sep, *p)) {
379 } else if (utf8_deg && *p == 0xc2 && *(p+1) == 0xb0) {
384 if (endptr) *endptr = p;
385 return f_flag ? 2 : 3;
388 if (endptr) *endptr = p;
393 strdmstod_2(gchar *nptr, gchar **endptr)
402 ret = strdmstod_1(&d, p, endptr, "dD@", 1);
406 if (endptr) *endptr = (char *)nptr;
414 ret = strdmstod_1(&m, p, endptr, "mM'", 0);
418 case 2: return d + m/60.0;
423 ret = strdmstod_1(&s, p, endptr, "sS\"", 0);
426 case 1: return d + m/60.0;
428 case 3: return d + m/60.0 + s/3600.0;
436 * format: / \s* [+-NSWE]? \s* ( d | D \s+ m | D \s+ M \s+ s ) [NSWE]? /x
437 * where D := / \d+[@d°]? /ix
438 * M := / \d+['m]? /ix
439 * d := / D | \d+[,.]\d+[@d°]? /ix
440 * m := / M | \d+[,.]\d+['m]? /ix
441 * s := / S | \d+[,.]\d+["s]? /ix
443 * and N or W are treated as positive, S or E are treated as negative,
444 * they may occur only once.
447 strdmstod(const gchar *nptr, gchar **endptr)
456 while(*p && isspace(*p))
461 *endptr = (char *)nptr;
465 if(strchr("nwseNWSE-+", *p)) {
470 d = strdmstod_2(p, &end);
471 if(p == end && d == 0.0) {
472 if(endptr) *endptr = end;
477 while(*p && isspace(*p))
482 if(*p && strchr("nwseNWSE", *p)) {
483 if(tolower(*p) == 's' || tolower(*p) == 'w')
488 if(tolower(*sign) == 's' || tolower(*sign) == 'w' || *sign == '-')
490 printf("s: %d\n", s);
493 if(endptr) *endptr = p;
497 double marc(double bf0, double n, double phi0, double phi)
499 return bf0 * (((1 + n + ((5 / 4) * (n * n)) + ((5 / 4) * (n * n * n))) * (phi - phi0))
500 - (((3 * n) + (3 * (n * n)) + ((21 / 8) * (n * n * n))) * (sin(phi - phi0)) * (cos(phi + phi0)))
501 + ((((15 / 8) * (n * n)) + ((15 / 8) * (n * n * n))) * (sin(2 * (phi - phi0))) * (cos(2 * (phi + phi0))))
502 - (((35 / 24) * (n * n * n)) * (sin(3 * (phi - phi0))) * (cos(3 * (phi + phi0)))));
505 gboolean os_grid_check_lat_lon(double lat, double lon)
507 // TODO - Check exact OS Grid range
508 if(lat < 50.0 || lat > 62 || lon < -7.5 || lon > 2.2 )
518 gboolean coord_system_check_lat_lon (gdouble lat, gdouble lon, gint *fallback_deg_format)
520 // Is the current coordinate system applicable to the provided lat and lon?
521 gboolean valid = FALSE;
528 valid = os_grid_check_lat_lon(lat, lon);
529 if(fallback_deg_format != NULL) *fallback_deg_format = DDPDDDDD;
539 gboolean convert_lat_long_to_os_grid(double lat, double lon, int *easting, int *northing)
541 if(!os_grid_check_lat_lon(lat, lon))
546 const double deg2rad = (2 * PI / 360);
548 const double phi = lat * deg2rad; // convert latitude to radians
549 const double lam = lon * deg2rad; // convert longitude to radians
550 const double a = 6377563.396; // OSGB semi-major axis
551 const double b = 6356256.91; // OSGB semi-minor axis
552 const double e0 = 400000; // easting of false origin
553 const double n0 = -100000; // northing of false origin
554 const double f0 = 0.9996012717; // OSGB scale factor on central meridian
555 const double e2 = 0.0066705397616; // OSGB eccentricity squared
556 const double lam0 = -0.034906585039886591; // OSGB false east
557 const double phi0 = 0.85521133347722145; // OSGB false north
558 const double af0 = a * f0;
559 const double bf0 = b * f0;
562 double slat2 = sin(phi) * sin(phi);
563 double nu = af0 / (sqrt(1 - (e2 * (slat2))));
564 double rho = (nu * (1 - e2)) / (1 - (e2 * slat2));
565 double eta2 = (nu / rho) - 1;
566 double p = lam - lam0;
567 double IV = nu * cos(phi);
568 double clat3 = pow(cos(phi), 3);
569 double tlat2 = tan(phi) * tan(phi);
570 double V = (nu / 6) * clat3 * ((nu / rho) - tlat2);
571 double clat5 = pow(cos(phi), 5);
572 double tlat4 = pow(tan(phi), 4);
573 double VI = (nu / 120) * clat5 * ((5 - (18 * tlat2)) + tlat4 + (14 * eta2) - (58 * tlat2 * eta2));
574 double east = e0 + (p * IV) + (pow(p, 3) * V) + (pow(p, 5) * VI);
577 double n = (af0 - bf0) / (af0 + bf0);
578 double M = marc(bf0, n, phi0, phi);
580 double II = (nu / 2) * sin(phi) * cos(phi);
581 double III = ((nu / 24) * sin(phi) * pow(cos(phi), 3)) * (5 - pow(tan(phi), 2) + (9 * eta2));
582 double IIIA = ((nu / 720) * sin(phi) * clat5) * (61 - (58 * tlat2) + tlat4);
583 double north = I + ((p * p) * II) + (pow(p, 4) * III) + (pow(p, 6) * IIIA);
585 // make whole number values
586 *easting = round(east); // round to whole number
587 *northing = round(north); // round to whole number
592 gboolean convert_os_grid_to_bng(gint easting, gint northing, gchar* bng)
594 gdouble eX = (gdouble)easting / 500000.0;
595 gdouble nX = (gdouble)northing / 500000.0;
596 gdouble tmp = floor(eX) - 5.0 * floor(nX) + 17.0;
600 nX = 5.0 * (nX - floor(nX));
601 eX = 20.0 - 5.0 * floor(nX) + floor(5.0 * (eX - floor(eX)));
602 if (eX > 7.5) eX = eX + 1; // I is not used
603 if (tmp > 7.5) tmp = tmp + 1; // I is not used
605 snprintf(eing, 12, "%u", easting);
606 snprintf(ning, 12, "%u", northing);
608 snprintf(eing, (_degformat == UK_NGR ? 5 : 4), "%s", eing+1);
609 snprintf(ning, (_degformat == UK_NGR ? 5 : 4), "%s", ning+1);
611 sprintf(bng, "%c%c%s%s",
621 gboolean convert_os_xy_to_latlon(const gchar *easting, const gchar *northing, gdouble *d_lat, gdouble *d_lon)
623 gint64 i64_n = g_ascii_strtoll (northing, NULL, 10);
624 gint64 i64_e = g_ascii_strtoll (easting, NULL, 10);
626 const double deg2rad = (2 * PI / 360);
628 const gdouble N = (gdouble)i64_n;
629 const gdouble E = (gdouble)i64_e;
631 const gdouble a = 6377563.396, b = 6356256.910; // Airy 1830 major & minor semi-axes
632 const gdouble F0 = 0.9996012717; // NatGrid scale factor on central meridian
633 const gdouble lat0 = 49*PI/180, lon0 = -2*PI/180; // NatGrid true origin
634 const gdouble N0 = -100000, E0 = 400000; // northing & easting of true origin, metres
635 const gdouble e2 = 1 - (b*b)/(a*a); // eccentricity squared
636 const gdouble n = (a-b)/(a+b), n2 = n*n, n3 = n*n*n;
638 gdouble lat=lat0, M=0;
640 lat = (N-N0-M)/(a*F0) + lat;
642 const gdouble Ma = (1 + n + (5/4)*n2 + (5/4)*n3) * (lat-lat0);
643 const gdouble Mb = (3*n + 3*n*n + (21/8)*n3) * sin(lat-lat0) * cos(lat+lat0);
644 const gdouble Mc = ((15/8)*n2 + (15/8)*n3) * sin(2*(lat-lat0)) * cos(2*(lat+lat0));
645 const gdouble Md = (35/24)*n3 * sin(3*(lat-lat0)) * cos(3*(lat+lat0));
646 M = b * F0 * (Ma - Mb + Mc - Md); // meridional arc
648 } while (N-N0-M >= 0.00001); // ie until < 0.01mm
650 const gdouble cosLat = cos(lat), sinLat = sin(lat);
651 const gdouble nu = a*F0/sqrt(1-e2*sinLat*sinLat); // transverse radius of curvature
652 const gdouble rho = a*F0*(1-e2)/pow(1-e2*sinLat*sinLat, 1.5); // meridional radius of curvature
653 const gdouble eta2 = nu/rho-1;
655 const gdouble tanLat = tan(lat);
656 const gdouble tan2lat = tanLat*tanLat, tan4lat = tan2lat*tan2lat, tan6lat = tan4lat*tan2lat;
657 const gdouble secLat = 1/cosLat;
658 const gdouble nu3 = nu*nu*nu, nu5 = nu3*nu*nu, nu7 = nu5*nu*nu;
659 const gdouble VII = tanLat/(2*rho*nu);
660 const gdouble VIII = tanLat/(24*rho*nu3)*(5+3*tan2lat+eta2-9*tan2lat*eta2);
661 const gdouble IX = tanLat/(720*rho*nu5)*(61+90*tan2lat+45*tan4lat);
662 const gdouble X = secLat/nu;
663 const gdouble XI = secLat/(6*nu3)*(nu/rho+2*tan2lat);
664 const gdouble XII = secLat/(120*nu5)*(5+28*tan2lat+24*tan4lat);
665 const gdouble XIIA = secLat/(5040*nu7)*(61+662*tan2lat+1320*tan4lat+720*tan6lat);
667 const gdouble dE = (E-E0), dE2 = dE*dE, dE3 = dE2*dE, dE4 = dE2*dE2, dE5 = dE3*dE2;
668 const gdouble dE6 = dE4*dE2, dE7 = dE5*dE2;
669 lat = lat - VII*dE2 + VIII*dE4 - IX*dE6;
670 const gdouble lon = lon0 + X*dE - XI*dE3 + XII*dE5 - XIIA*dE7;
672 *d_lon = lon / deg2rad;
673 *d_lat = lat / deg2rad;
678 gboolean convert_os_ngr_to_latlon(const gchar *text, gdouble *d_lat, gdouble *d_lon)
680 // get numeric values of letter references, mapping A->0, B->1, C->2, etc:
684 gchar s_e[6], s_n[6];
685 gchar easting[7], northing[7];
690 if( ((gchar)text[0])>='a' && ((gchar)text[0]) <= 'z' )
691 l1 = text[0] - (gint)'a'; // lower case
692 else if( ((gchar)text[0])>='A' && ((gchar)text[0]) <= 'Z' )
693 l1 = text[0] - (gint)'A'; // upper case
695 return FALSE; // Not a letter - invalid grid ref
697 if( ((gchar)text[1])>='a' && ((gchar)text[1]) <= 'z' )
698 l2 = text[1] - (gint)'a'; // lower case
699 else if( ((gchar)text[1])>='A' && ((gchar)text[1]) <= 'Z' )
700 l2 = text[1] - (gint)'A'; // upper case
702 return FALSE; // Not a letter - invalid grid ref
705 // shuffle down letters after 'I' since 'I' is not used in grid:
709 // convert grid letters into 100km-square indexes from false origin (grid square SV):
710 gdouble e = ((l1-2)%5)*5 + (l2%5);
711 gdouble n = (19-floor(l1/5)*5) - floor(l2/5);
713 // skip grid letters to get numeric part of ref, stripping any spaces:
714 gchar *gridref = (gchar*)(text+2);
716 // user may have entered a space, so remove any spaces
717 while(gridref[0] == ' ')
719 gridref = (gchar*)(text+1);
723 // floor the length incase a space has been added
724 const gint len = (gint)floor((gdouble)strlen(gridref)/2.00); // normally this will be 4, often 3
725 if(len>5) return FALSE;
729 snprintf(s_e, len+1, "%s", gridref);
731 while( (gchar)((gint)gridref+len) == ' ' )
732 gridref = (gchar*)(gridref+1); // Allow for a space
734 snprintf(s_n, len+1, "%s", gridref+len);
736 i64_e = g_ascii_strtoll (s_e, NULL, 10);
737 i64_n = g_ascii_strtoll (s_n, NULL, 10);
739 // Move to most significate values
740 i64_e *= pow(10, 5-len);
741 i64_n *= pow(10, 5-len);
744 // append numeric part of references to grid index:
745 e = (e*100000) + (gdouble)i64_e;
746 n = (n*100000) + (gdouble)i64_n;
748 snprintf(easting, 7, "%06u", (gint)e);
749 snprintf(northing, 7, "%06u", (gint)n);
751 convert_os_xy_to_latlon(easting, northing, d_lat, d_lon);
757 // Attempt to convert any user entered grid reference to a double lat/lon
758 // return TRUE on valid
759 gboolean parse_coords(const gchar* txt_lat, const gchar* txt_lon, gdouble* lat, gdouble* lon)
761 gboolean valid = FALSE;
763 // UK_NGR starts with two letters, and then all numbers - it may contain spaces - no lon will be entered
764 if( _degformat == UK_NGR || _degformat == UK_NGR6 )
766 valid = convert_os_ngr_to_latlon(txt_lat, lat, lon);
768 if(!valid || *lat < -90. || *lat > 90.) { valid = FALSE; }
769 else if(*lon < -180. || *lon > 180.) { valid = FALSE; }
771 // UK_OSGB contains two 6 digit integers
772 else if( _degformat == UK_OSGB)
774 valid = convert_os_xy_to_latlon(txt_lat, txt_lon, lat, lon);
776 if(!valid || *lat < -90. || *lat > 90.) { valid = FALSE; }
777 else if(*lon < -180. || *lon > 180.) { valid = FALSE; }
780 else if( _degformat == IARU_LOC)
782 valid = convert_iaru_loc_to_lat_lon(txt_lat, lat, lon);
784 if(!valid || *lat < -90. || *lat > 90.) { valid = FALSE; }
785 else if(*lon < -180. || *lon > 180.) { valid = FALSE; }
787 // It must either be invalid, or a lat/lon format
791 *lat = strdmstod(txt_lat, &error_check);
793 if(txt_lat == error_check || *lat < -90. || *lat > 90.) { valid = FALSE; }
794 else { valid = TRUE; }
798 *lon = strdmstod(txt_lon, &error_check);
800 if(txt_lon == error_check || *lon < -180. || *lon > 180.) { valid = FALSE; }
809 gboolean convert_iaru_loc_to_lat_lon(const gchar* txt_lon, gdouble* lat, gdouble* lon)
820 if(strlen(txt_lon) >= 1)
822 if( ((gchar)txt_lon[0])>='a' && ((gchar)txt_lon[0]) <= 'z' )
823 u_first = txt_lon[0] - (gint)'a'; // lower case
824 else if( ((gchar)txt_lon[0])>='A' && ((gchar)txt_lon[0]) <= 'Z' )
825 u_first = txt_lon[0] - (gint)'A'; // upper case
828 if(strlen(txt_lon) >= 2)
830 if( ((gchar)txt_lon[1])>='a' && ((gchar)txt_lon[1]) <= 'z' )
831 u_second = txt_lon[1] - (gint)'a'; // lower case
832 else if( ((gchar)txt_lon[1])>='A' && ((gchar)txt_lon[1]) <= 'Z' )
833 u_second = txt_lon[1] - (gint)'A'; // upper case
836 if(strlen(txt_lon) >= 3)
837 u_third = txt_lon[2] - (gint)'0';
839 if(strlen(txt_lon) >= 4)
840 u_fourth = txt_lon[3] - (gint)'0';
842 if(strlen(txt_lon) >= 5)
844 if( ((gchar)txt_lon[4])>='a' && ((gchar)txt_lon[4]) <= 'z' )
845 u_fifth = txt_lon[4] - (gint)'a'; // lower case
846 else if( ((gchar)txt_lon[4])>='A' && ((gchar)txt_lon[4]) <= 'Z' )
847 u_fifth = txt_lon[4] - (gint)'A'; // upper case
850 if(strlen(txt_lon) >= 6)
852 if( ((gchar)txt_lon[5])>='a' && ((gchar)txt_lon[5]) <= 'z' )
853 u_sixth = txt_lon[5] - (gint)'a'; // lower case
854 else if( ((gchar)txt_lon[5])>='A' && ((gchar)txt_lon[5]) <= 'Z' )
855 u_sixth = txt_lon[5] - (gint)'A'; // upper case
859 if(strlen(txt_lon) >= 7)
860 u_seventh= txt_lon[6] - (gint)'0';
862 if(strlen(txt_lon) >= 8)
863 u_eighth = txt_lon[7] - (gint)'0';
865 *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;
866 *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;
871 void convert_lat_lon_to_iaru_loc(gdouble d_lat, gdouble d_lon, gchar *loc)
873 const gdouble d_a_lat = (d_lat+90.0);
874 const gdouble d_a_lon = (d_lon+180.0);
876 const gint i_first = (gint)floor(d_a_lon/20.0);
877 const gint i_second = (gint)floor(d_a_lat/10.0);
878 const gint i_third = (gint)floor((d_a_lon - (20.0*i_first))/2.0);
879 const gint i_fourth = (gint)floor((d_a_lat - (10.0*i_second)));
880 const gint i_fifth = (gint)floor((d_a_lon - (20.0*i_first) - (2.0*i_third))/(5.0/60.0));
881 const gint i_sixth = (gint)floor((d_a_lat - (10.0*i_second) - (i_fourth))/(2.5/60.0));
883 const gint i_seventh = (gint)floor((d_a_lon - (20.0*i_first) - (2.0*i_third)
884 - ((2.0/24.0)*i_fifth))/(2.0/240.0) );
885 const gint i_eighth = (gint)floor((d_a_lat - (10.0*i_second) - (i_fourth)
886 - ((1.0/24.0)*i_sixth))/(1.0/240.0));
889 sprintf(loc, "%c%c%u%u%c%c%u%u",
900 gboolean convert_lat_lon_to_bng(gdouble lat, gdouble lon, gchar* bng)
902 gint easting, northing;
904 if( convert_lat_long_to_os_grid(lat, lon, &easting, &northing) )
906 if( convert_os_grid_to_bng(easting, northing, bng) )
916 void format_lat_lon(gdouble d_lat, gdouble d_lon, gchar* lat, gchar* lon)
925 if(convert_lat_long_to_os_grid(d_lat, d_lon, &east, &north))
927 sprintf(lat, "%06d", east);
928 sprintf(lon, "%06d", north);
932 // Failed (possibly out of range), so use defaults
933 lat_format(d_lat, lat);
934 lon_format(d_lon, lon);
940 if(convert_lat_lon_to_bng(d_lat, d_lon, lat))
946 // Failed (possibly out of range), so use defaults
947 lat_format(d_lat, lat);
948 lat_format(d_lon, lon);
953 convert_lat_lon_to_iaru_loc(d_lat, d_lon, lat);
958 lat_format(d_lat, lat);
959 lon_format(d_lon, lon);
965 /* Custom version of g_ascii_strtoll, since Gregale does not support
968 g_ascii_strtoll(const gchar *nptr, gchar **endptr, guint base)
970 gchar *minus = g_strstr_len(nptr, -1, "");
972 return -g_ascii_strtoull(minus + 1, endptr, base);
974 return g_ascii_strtoull(nptr, endptr, base);
984 { "-12.345d", -12.345 },
985 { "12.345 E", -12.345 },
986 { "12d34m", 12.5666667 },
987 { "N 12 34", 12.5666667 },
988 { "S 12d34.56m", -12.576 },
989 { "W 12 34.56", 12.576 },
990 { "E 12d34m56s", -12.582222 },
991 { "12 34 56 S", -12.582222 },
992 { "12 34 56", 12.582222 },
993 { "12d34m56.7s E", -12.582417 },
994 { "12 34 56.7 W", 12.582417 },
995 { "12° 34 56.7 W", 12.582417 },
1008 for (t = t_cases; t->fmt; t++) {
1012 v = strdmstod(t->fmt, &endp);
1013 if (endp == t->fmt || *endp != 0) {
1014 fprintf(stderr, "FAIL syntax %s\n", t->fmt);
1016 } else if (fabs(v - t->value) > 0.000001) {
1017 fprintf(stderr, "FAIL value %s -> %.10g (%.10g)\n",
1018 t->fmt, v, t->value);
1026 fprintf(stderr, "ALL TESTS OK\n");
1028 fprintf(stderr, "FAIL %d, OK %d\n", fail, ok);