]> git.street.me.uk Git - andy/viking.git/blob - src/vikgotoxmltool.c
Add support for XML attribute based lat/lon values
[andy/viking.git] / src / vikgotoxmltool.c
1 /*
2  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3  *
4  * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5  * Copyright (C) 2009, Guilhem Bonnefille <guilhem.bonnefille@gmail.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  * Created by Quy Tonthat <qtonthat@gmail.com>
22  */
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 #include <stdlib.h>
27 #include <stdio.h>
28 #include <string.h>
29 #ifdef HAVE_MATH_H
30 #include "math.h"
31 #endif
32 #include <glib.h>
33 #include <glib/gstdio.h>
34 #include <glib/gprintf.h>
35 #include <glib/gi18n.h>
36
37 #include "viking.h"
38
39 #include "vikgotoxmltool.h"
40
41
42 static void _goto_xml_tool_class_init ( VikGotoXmlToolClass *klass );
43 static void _goto_xml_tool_init ( VikGotoXmlTool *self );
44
45 static void _goto_xml_tool_finalize ( GObject *gob );
46
47 static gchar *_goto_xml_tool_get_url_format ( VikGotoTool *self );
48 static gboolean _goto_xml_tool_parse_file_for_latlon(VikGotoTool *self, gchar *filename, struct LatLon *ll);
49
50 typedef struct _VikGotoXmlToolPrivate VikGotoXmlToolPrivate;
51
52 struct _VikGotoXmlToolPrivate
53 {
54   gchar *url_format;
55   gchar *lat_path;
56   gchar *lat_attr;
57   gchar *lon_path;
58   gchar *lon_attr;
59   
60   struct LatLon ll;
61 };
62
63 #define GOTO_XML_TOOL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
64                                         VIK_GOTO_XML_TOOL_TYPE,          \
65                                         VikGotoXmlToolPrivate))
66
67 GType vik_goto_xml_tool_get_type()
68 {
69   static GType w_type = 0;
70
71   if (!w_type)
72   {
73     static const GTypeInfo w_info = 
74     {
75       sizeof (VikGotoXmlToolClass),
76       NULL, /* base_init */
77       NULL, /* base_finalize */
78       (GClassInitFunc) _goto_xml_tool_class_init,
79       NULL, /* class_finalize */
80       NULL, /* class_data */
81       sizeof (VikGotoXmlTool),
82       0,
83       (GInstanceInitFunc) _goto_xml_tool_init,
84     };
85     w_type = g_type_register_static ( VIK_GOTO_TOOL_TYPE, "VikGotoXmlTool", &w_info, 0 );
86   }
87
88   return w_type;
89 }
90
91 enum
92 {
93   PROP_0,
94
95   PROP_URL_FORMAT,
96   PROP_LAT_PATH,
97   PROP_LAT_ATTR,
98   PROP_LON_PATH,
99   PROP_LON_ATTR,
100 };
101
102 static void
103 _goto_xml_tool_set_property (GObject      *object,
104                              guint         property_id,
105                              const GValue *value,
106                              GParamSpec   *pspec)
107 {
108   VikGotoXmlTool *self = VIK_GOTO_XML_TOOL (object);
109   VikGotoXmlToolPrivate *priv = GOTO_XML_TOOL_GET_PRIVATE (self);
110
111   switch (property_id)
112     {
113     case PROP_URL_FORMAT:
114       g_free (priv->url_format);
115       priv->url_format = g_value_dup_string (value);
116       break;
117
118     case PROP_LAT_PATH:
119       g_free (priv->lat_path);
120       priv->lat_path = g_value_dup_string (value);
121       break;
122
123     case PROP_LAT_ATTR:
124       g_free (priv->lat_attr);
125       priv->lat_attr = g_value_dup_string (value);
126       break;
127
128     case PROP_LON_PATH:
129       g_free (priv->lon_path);
130       priv->lon_path = g_value_dup_string (value);
131       break;
132
133     case PROP_LON_ATTR:
134       g_free (priv->lon_attr);
135       priv->lon_attr = g_value_dup_string (value);
136       break;
137
138     default:
139       /* We don't have any other property... */
140       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
141       break;
142     }
143 }
144
145 static void
146 _goto_xml_tool_get_property (GObject    *object,
147                              guint       property_id,
148                              GValue     *value,
149                              GParamSpec *pspec)
150 {
151   VikGotoXmlTool *self = VIK_GOTO_XML_TOOL (object);
152   VikGotoXmlToolPrivate *priv = GOTO_XML_TOOL_GET_PRIVATE (self);
153
154   switch (property_id)
155     {
156     case PROP_URL_FORMAT:
157       g_value_set_string (value, priv->url_format);
158       break;
159
160     case PROP_LAT_PATH:
161       g_value_set_string (value, priv->lat_path);
162       break;
163
164     case PROP_LAT_ATTR:
165       g_value_set_string (value, priv->lat_attr);
166       break;
167
168     case PROP_LON_PATH:
169       g_value_set_string (value, priv->lon_path);
170       break;
171
172     case PROP_LON_ATTR:
173       g_value_set_string (value, priv->lon_attr);
174       break;
175
176     default:
177       /* We don't have any other property... */
178       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
179       break;
180     }
181 }
182
183 static void _goto_xml_tool_class_init ( VikGotoXmlToolClass *klass )
184 {
185   GObjectClass *object_class;
186   VikGotoToolClass *parent_class;
187   GParamSpec *pspec;
188
189   object_class = G_OBJECT_CLASS (klass);
190
191   object_class->finalize = _goto_xml_tool_finalize;
192   object_class->set_property = _goto_xml_tool_set_property;
193   object_class->get_property = _goto_xml_tool_get_property;
194
195
196   pspec = g_param_spec_string ("url-format",
197                                "URL format",
198                                "The format of the URL",
199                                "<no-set>" /* default value */,
200                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
201   g_object_class_install_property (object_class,
202                                    PROP_URL_FORMAT,
203                                    pspec);
204
205   pspec = g_param_spec_string ("lat-path",
206                                "Latitude path",
207                                "XPath of the latitude",
208                                "<no-set>" /* default value */,
209                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
210   g_object_class_install_property (object_class,
211                                    PROP_LAT_PATH,
212                                    pspec);
213
214   pspec = g_param_spec_string ("lat-attr",
215                                "Latitude attribute",
216                                "XML attribute of the latitude",
217                                NULL /* default value */,
218                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
219   g_object_class_install_property (object_class,
220                                    PROP_LAT_ATTR,
221                                    pspec);
222
223   pspec = g_param_spec_string ("lon-path",
224                                "Longitude path",
225                                "XPath of the longitude",
226                                "<no-set>" /* default value */,
227                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
228   g_object_class_install_property (object_class,
229                                    PROP_LON_PATH,
230                                    pspec);
231
232   pspec = g_param_spec_string ("lon-attr",
233                                "Longitude attribute",
234                                "XML attribute of the longitude",
235                                NULL /* default value */,
236                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
237   g_object_class_install_property (object_class,
238                                    PROP_LON_ATTR,
239                                    pspec);
240
241   parent_class = VIK_GOTO_TOOL_CLASS (klass);
242
243   parent_class->get_url_format = _goto_xml_tool_get_url_format;
244   parent_class->parse_file_for_latlon = _goto_xml_tool_parse_file_for_latlon;
245
246   g_type_class_add_private (klass, sizeof (VikGotoXmlToolPrivate));
247 }
248
249 VikGotoXmlTool *vik_goto_xml_tool_new ()
250 {
251   return VIK_GOTO_XML_TOOL ( g_object_new ( VIK_GOTO_XML_TOOL_TYPE, "label", "Google", NULL ) );
252 }
253
254 static void _goto_xml_tool_init ( VikGotoXmlTool *self )
255 {
256   VikGotoXmlToolPrivate *priv = GOTO_XML_TOOL_GET_PRIVATE (self);
257   priv->url_format = NULL;
258   priv->lat_path = NULL;
259   priv->lat_attr = NULL;
260   priv->lon_path = NULL;
261   priv->lon_attr = NULL;
262   // 
263   priv->ll.lat = NAN;
264   priv->ll.lon = NAN;
265 }
266
267 static void _goto_xml_tool_finalize ( GObject *gob )
268 {
269   G_OBJECT_GET_CLASS(gob)->finalize(gob);
270 }
271
272 static gboolean
273 stack_is_path (const GSList *stack,
274                const gchar  *path)
275 {
276   gboolean equal = TRUE;
277   int stack_len = g_list_length((GList *)stack);
278   int i = 0;
279   i = stack_len - 1;
280   while (equal == TRUE && i >= 0)
281   {
282     if (*path != '/')
283       equal = FALSE;
284     else
285       path++;
286     const gchar *current = g_list_nth_data((GList *)stack, i);
287     size_t len = strlen(current);
288     if (strncmp(path, current, len) != 0 )
289       equal = FALSE;
290     else
291     {
292       path += len;
293     }
294     i--;
295   }
296   if (*path != '\0')
297     equal = FALSE;
298   return equal;
299 }
300
301 /* Called for open tags <foo bar="baz"> */
302 static void
303 _start_element (GMarkupParseContext *context,
304                 const gchar         *element_name,
305                 const gchar        **attribute_names,
306                 const gchar        **attribute_values,
307                 gpointer             user_data,
308                 GError             **error)
309 {
310   VikGotoXmlTool *self = VIK_GOTO_XML_TOOL (user_data);
311   VikGotoXmlToolPrivate *priv = GOTO_XML_TOOL_GET_PRIVATE (self);
312   const GSList *stack = g_markup_parse_context_get_element_stack (context);
313   /* Longitude */
314   if (priv->lon_attr != NULL && isnan(priv->ll.lon) && stack_is_path (stack, priv->lon_path))
315         {
316                 int i=0;
317                 while (attribute_names[i] != NULL)
318                 {
319                         if (strcmp (attribute_names[i], priv->lon_attr) == 0)
320                         {
321                                 priv->ll.lon = g_ascii_strtod(attribute_values[i], NULL);
322                         }
323                         i++;
324                 }
325         }
326   /* Latitude */
327   if (priv->lat_attr != NULL && isnan(priv->ll.lat) && stack_is_path (stack, priv->lat_path))
328         {
329                 int i=0;
330                 while (attribute_names[i] != NULL)
331                 {
332                         if (strcmp (attribute_names[i], priv->lat_attr) == 0)
333                         {
334                                 priv->ll.lat = g_ascii_strtod(attribute_values[i], NULL);
335                         }
336                         i++;
337                 }
338         }
339 }
340
341 /* Called for character data */
342 /* text is not nul-terminated */
343 static void
344 _text (GMarkupParseContext *context,
345        const gchar         *text,
346        gsize                text_len,  
347        gpointer             user_data,
348        GError             **error)
349 {
350   VikGotoXmlTool *self = VIK_GOTO_XML_TOOL (user_data);
351   VikGotoXmlToolPrivate *priv = GOTO_XML_TOOL_GET_PRIVATE (self);
352   const GSList *stack = g_markup_parse_context_get_element_stack (context);
353   gchar *textl = g_strndup(text, text_len);
354   /* Store only first result */
355         if (priv->lat_attr == NULL && isnan(priv->ll.lat) && stack_is_path (stack, priv->lat_path))
356         {
357     priv->ll.lat = g_ascii_strtod(textl, NULL);
358         }
359         if (priv->lon_attr == NULL && isnan(priv->ll.lon) && stack_is_path (stack, priv->lon_path))
360         {
361     priv->ll.lon = g_ascii_strtod(textl, NULL);
362         }
363   g_free(textl);
364 }
365
366 static gboolean
367 _goto_xml_tool_parse_file_for_latlon(VikGotoTool *self, gchar *filename, struct LatLon *ll)
368 {
369         GMarkupParser xml_parser;
370         GMarkupParseContext *xml_context;
371         GError *error;
372         VikGotoXmlToolPrivate *priv = GOTO_XML_TOOL_GET_PRIVATE (self);
373   g_return_val_if_fail(priv != NULL, FALSE);
374
375         FILE *file = g_fopen (filename, "r");
376         if (file == NULL)
377                 /* TODO emit warning */
378                 return FALSE;
379         
380         /* setup context parse (ie callbacks) */
381         if (priv->lat_attr != NULL || priv->lon_attr != NULL)
382     // At least one coordinate uses an attribute
383     xml_parser.start_element = &_start_element;
384   else
385     xml_parser.start_element = NULL;
386         xml_parser.end_element = NULL;
387         if (priv->lat_attr == NULL || priv->lon_attr == NULL)
388     // At least one coordinate uses a raw element
389     xml_parser.text = &_text;
390   else
391     xml_parser.text = NULL;
392         xml_parser.passthrough = NULL;
393         xml_parser.error = NULL;
394         
395         xml_context = g_markup_parse_context_new(&xml_parser, 0, self, NULL);
396
397         /* setup result */
398         priv->ll.lat = NAN;
399         priv->ll.lon = NAN;
400         
401         gchar buff[BUFSIZ];
402         size_t nb;
403         while ((nb = fread (buff, sizeof(gchar), BUFSIZ, file)) > 0)
404         {
405                 if (!g_markup_parse_context_parse(xml_context, buff, nb, &error))
406                         fprintf(stderr, "%s: parsing error.\n", __FUNCTION__);
407         }
408         /* cleanup */
409         if (!g_markup_parse_context_end_parse(xml_context, &error))
410                 fprintf(stderr, "%s: errors occurred reading file.\n", __FUNCTION__);
411         
412         g_markup_parse_context_free(xml_context);
413         fclose (file);
414   
415   if (ll != NULL)
416   {
417     *ll = priv->ll;
418   }
419   
420   if (isnan(priv->ll.lat) || isnan(priv->ll.lat))
421                 /* At least one coordinate not found */
422                 return FALSE;
423         else
424                 return TRUE;
425 }
426
427 static gchar *
428 _goto_xml_tool_get_url_format ( VikGotoTool *self )
429 {
430   VikGotoXmlToolPrivate *priv = GOTO_XML_TOOL_GET_PRIVATE (self);
431   g_return_val_if_fail(priv != NULL, NULL);
432   return priv->url_format;
433 }