Saving work, you have been warned
[libgeouri:libgeouri.git] / src / libgeouri.c
1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 2; tab-width: 2 -*- */
2 /*
3  * libgeouri.c
4  * Copyright (C) Łukasz Jernaś 2010 <deejay1@srem.org>
5  * 
6  * libgeouri is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU Lesser General Public License as published
8  * by the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  * 
11  * libgeouri is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14  * See the GNU Lesser General Public License for more details.
15  * 
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
18  */
19
20 #include "libgeouri.h"
21 #include <glib.h>
22 #include <gmp.h>
23
24 typedef struct _Altitude Altitude;
25 typedef struct _Uncertainity Uncertainity;
26
27 struct _Altitude
28 {
29         gboolean is_set;
30         mpf_t value;
31 };
32
33 struct _Uncertainity
34 {
35         gboolean is_set;
36         mpf_t value;
37 };
38
39 struct _GeoUri
40 {
41         mpf_t lat;
42         mpf_t lon;
43         Altitude *alt;
44         Uncertainity *u;
45 };
46
47 static void geouri_init (GeoUri *uri);
48
49
50 /**
51  * geouri_parse_string:
52  * @uri: a #GeoUri
53  * @data: (in): a string representing a geo URI
54  * @error: (out) (allow-none): return location for a #GError, or %NULL
55  *
56  * Creates a new geo URI from a string.
57  *
58  * Return value: %TRUE if an URI could be parsed properly
59  *
60  */
61 gboolean
62 geouri_parse_string (GeoUri *uri, const gchar *data, GError **error)
63 {
64         gchar * scheme;
65         gchar ** array;
66         gdouble tmp_lat, tmp_lon, tmp_alt;
67         scheme = g_uri_parse_scheme (data);
68         if (g_strcmp0 (scheme, "geo") != 0 && scheme != NULL)
69         {
70                 g_set_error (error, GEOURI_ERROR, GEOURI_ERROR_INVALID_SCHEME,
71                              "Invalid scheme in URI. Specified scheme was %s", scheme);
72                 g_free (scheme);
73                 return FALSE;
74         }
75         else if (scheme == NULL)
76         {
77                 g_set_error (error, GEOURI_ERROR, GEOURI_ERROR_INVALID_URI,
78                              "URI doesn't have a scheme");
79                 g_free (scheme);
80                 return FALSE;
81         }
82         g_free (scheme);
83
84         GRegex *regex;
85         GMatchInfo *match_info;
86         // Grab the lat, lon, alt values from the string
87         regex = g_regex_new ("-?\\d{1,3}(?:\\.\\d+)?(?:,-?\\d{1,3}(?:\\.\\d+)?){1,2}",
88                              0, 0, NULL);
89         g_regex_match (regex, data, 0, &match_info);
90         g_match_info_matches (match_info);
91   gchar *params = g_match_info_fetch (match_info, 0);
92         if (G_UNLIKELY(params == NULL))
93           {
94             g_set_error (error, GEOURI_ERROR, GEOURI_ERROR_INVALID_URI,
95                              "Error parsing URI %s", data);
96                         return FALSE;
97                 }
98         g_match_info_free (match_info);
99         g_regex_unref (regex);
100   //FIXME
101         array = g_strsplit (params, ",", 3);
102         if (array[0] != NULL)
103                 tmp_lat = g_ascii_strtod(array[0], NULL);
104         if (array[1] != NULL)
105                 tmp_lon = g_ascii_strtod(array[1], NULL);
106         if (array[2] != NULL)
107         {
108                 // We can always set an altitude to any arbitrary double value
109                 tmp_alt = g_ascii_strtod(array[2], NULL);
110           geouri_set_altitude (uri, tmp_alt, error);
111         }
112
113         g_free (params);
114         g_strfreev (array);
115
116         if (!geouri_set_latitude (uri, tmp_lat, error))
117                 return FALSE;
118         if (!geouri_set_longitude (uri, tmp_lon, error))
119                 return FALSE;
120
121         return TRUE;
122 }
123
124 /**
125  * geouri_get_latitude:
126  * @uri: (in): a #GeoUri
127  *
128  * Return value: the latitude for the specified #GeoUri
129  */
130 gdouble
131 geouri_get_latitude (GeoUri *uri, GError **error)
132 {
133         return mpf_get_d (uri->lat);
134 }
135
136 gboolean
137 geouri_set_latitude (GeoUri *uri, gdouble lat, GError **error)
138 {
139         if (lat < -90.0 || lat > 90.0)
140         {
141                 g_set_error (error, GEOURI_ERROR, GEOURI_ERROR_INVALID_COORDS,
142                              "Latitude must be between -90.0 and 90.0");
143                 return FALSE;
144         }
145         if (lat == -90.0 || lat == 90.0)
146                 mpf_set_d (uri->lon, 0);
147
148         mpf_set_d (uri->lat, lat);
149
150         return TRUE;
151 }
152
153 /**
154  * geouri_get_longitude:
155  * @uri: (in): a #GeoUri
156  *
157  * Return value: the longitude for the specified #GeoUri
158  */
159 gdouble
160 geouri_get_longitude (GeoUri *uri, GError **error)
161 {
162         return mpf_get_d (uri->lon);
163 }
164
165 gboolean
166 geouri_set_longitude (GeoUri *uri, gdouble lon, GError **error)
167 {
168         if (lon < -180.0 || lon > 180.0)
169         {
170                 g_set_error (error, GEOURI_ERROR, GEOURI_ERROR_INVALID_COORDS,
171                              "Longitude must be between -180.0 and 180.0");
172                 return FALSE;
173         }
174
175         mpf_set_d (uri->lon, lon);
176
177         return TRUE;
178 }
179
180 /**
181  * geouri_get_altitude:
182  * @uri: (in) (transfer none): a #GeoUri
183  * @alt: (out): an altitude
184  * @error: (out): return location for a #GError, or %NULL
185  */
186 gboolean
187 geouri_get_altitude (GeoUri *uri, gdouble *alt, GError **error)
188 {
189         if (!uri->alt->is_set)
190         {
191                 g_set_error (error, GEOURI_ERROR, GEOURI_ERROR_ALTITUDE_UNDEFINED,
192                              "Altitude for the given URI isn't defined");
193                 return FALSE;
194         }
195
196         *alt = mpf_get_d (uri->alt->value);
197
198         return TRUE;
199 }
200
201 gboolean
202 geouri_set_altitude (GeoUri *uri, gdouble alt, GError **error)
203 {
204         uri->alt->is_set = TRUE;
205         mpf_set_d (uri->alt->value, alt);
206         return TRUE;
207 }
208
209 /**
210  * geouri_tostr:
211  * @uri: (in): a #GeoUri
212  *
213  * Returns a string representing a #GeoUri. The returned string should be freed
214  * with g_free() when no longer needed.
215  *
216  * Return value: a newly allocated string representing a geo URI
217  */
218 gchar *
219 geouri_tostr (GeoUri *uri, GError **error)
220 {
221         char buf[G_ASCII_DTOSTR_BUF_SIZE];
222         char buf2[G_ASCII_DTOSTR_BUF_SIZE];
223         char buf3[G_ASCII_DTOSTR_BUF_SIZE];
224         gchar * string;
225         string = g_strconcat("geo:", g_ascii_dtostr (buf, sizeof (buf), mpf_get_d (uri->lat)),
226                              ",", g_ascii_dtostr (buf2, sizeof (buf2),mpf_get_d (uri->lon)),
227                              NULL);
228         if (uri->alt->is_set)
229                 string = g_strconcat(string, ",",
230                                      g_ascii_dtostr (buf3, sizeof (buf3), mpf_get_d (uri->alt->value)),
231                                      NULL);
232         return string;
233         return NULL;
234 }
235
236 /**
237  * geouri_cmp:
238  * @uri1: a #GeoUri or %NULL
239  * @uri2: another #GeoUri or %NULL
240  *
241  * Compares #uri1 and #uri2 according to RFC. Handles %NULL gracefully by sorting
242  * it before non-%NULL URI-s.
243  *
244  * Return value: -1, 0 or 1, if #uri1 is <, == or > than #uri2.
245  */
246 int
247 geouri_cmp (GeoUri *uri1, GeoUri *uri2)
248 {
249         return -20;
250 }
251
252 /* Required functions */
253
254 GQuark
255 geouri_error_quark (void)
256 {
257         return g_quark_from_static_string ("geouri_error_quark");
258 }
259
260 void
261 geouri_init(GeoUri *uri)
262 {
263         mpf_init (uri->lat);
264         mpf_init (uri->lon);
265         mpf_set_d (uri->lat, 0);
266         mpf_set_d (uri->lon, 0);
267         uri->alt = g_new (Altitude, 1);
268         uri->alt->is_set=FALSE;
269   mpf_init (uri->alt->value);
270
271         uri->u = g_new (Uncertainity, 1);
272         uri->u->is_set = FALSE;
273         mpf_init (uri->u->value);
274 }
275
276 /**
277  * geouri_new:
278  *
279  * Creates a new empty geo URI object with reference to the 0,0 position.
280  *
281  * Use geouri_parse_string() to create a position or the geouri_set_latitude(),
282  * geouri_set_longitude() and geouri_set_altitude() functions.
283  *
284  * Return value: an empty #GeoUri
285  */
286 GeoUri *
287 geouri_new (void)
288 {
289         GeoUri *uri;
290         uri = g_new (GeoUri, 1);
291         geouri_init (uri);
292         return uri;
293 }
294
295 /**
296  * geouri_free:
297  * @uri: a #GeoUri
298  *
299  * Frees a #GeoUri
300  */
301 void
302 geouri_free(GeoUri *uri)
303 {
304         if (!uri)
305                 return;
306         mpf_clear (uri->alt->value);
307         mpf_clear (uri->u->value);
308         mpf_clear (uri->lat);
309         mpf_clear (uri->lon);
310         g_free(uri->alt);
311         g_free(uri->u);
312         g_free(uri);
313 }