]> git.street.me.uk Git - andy/viking.git/blob - src/vikxmlsearchtool.c
Create the geonames search as an VikXmlSearchTool
[andy/viking.git] / src / vikxmlsearchtool.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 #include "util.h"
39 #include "curl_download.h"
40
41 #include "vikxmlsearchtool.h"
42
43
44 static void vik_xml_search_tool_class_init ( VikXmlSearchToolClass *klass );
45 static void vik_xml_search_tool_init ( VikXmlSearchTool *vwd );
46
47 static void vik_xml_search_tool_finalize ( GObject *gob );
48
49 static int vik_xml_search_tool_get_coord ( VikSearchTool *self, VikWindow *vw, VikViewport *vvp, gchar *srch_str, VikCoord *coord );
50
51 typedef struct _VikXmlSearchToolPrivate VikXmlSearchToolPrivate;
52
53 struct _VikXmlSearchToolPrivate
54 {
55   gchar *url_format;
56   gchar *lat_path;
57   gchar *lon_path;
58   
59   struct LatLon ll;
60 };
61
62 #define XML_SEARCH_TOOL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
63                                         VIK_XML_SEARCH_TOOL_TYPE,          \
64                                         VikXmlSearchToolPrivate))
65
66 GType vik_xml_search_tool_get_type()
67 {
68   static GType w_type = 0;
69
70   if (!w_type)
71   {
72     static const GTypeInfo w_info = 
73     {
74       sizeof (VikXmlSearchToolClass),
75       NULL, /* base_init */
76       NULL, /* base_finalize */
77       (GClassInitFunc) vik_xml_search_tool_class_init,
78       NULL, /* class_finalize */
79       NULL, /* class_data */
80       sizeof (VikXmlSearchTool),
81       0,
82       (GInstanceInitFunc) vik_xml_search_tool_init,
83     };
84     w_type = g_type_register_static ( VIK_SEARCH_TOOL_TYPE, "VikXmlSearchTool", &w_info, 0 );
85   }
86
87   return w_type;
88 }
89
90 enum
91 {
92   PROP_0,
93
94   PROP_URL_FORMAT,
95   PROP_LAT_PATH,
96   PROP_LON_PATH,
97 };
98
99 static void
100 xml_search_tool_set_property (GObject      *object,
101                               guint         property_id,
102                               const GValue *value,
103                               GParamSpec   *pspec)
104 {
105   VikXmlSearchTool *self = VIK_XML_SEARCH_TOOL (object);
106   VikXmlSearchToolPrivate *priv = XML_SEARCH_TOOL_GET_PRIVATE (self);
107
108   switch (property_id)
109     {
110     case PROP_URL_FORMAT:
111       g_free (priv->url_format);
112       priv->url_format = g_value_dup_string (value);
113       break;
114
115     case PROP_LAT_PATH:
116       g_free (priv->lat_path);
117       priv->lat_path = g_value_dup_string (value);
118       break;
119
120     case PROP_LON_PATH:
121       g_free (priv->lon_path);
122       priv->lon_path = g_value_dup_string (value);
123       break;
124
125     default:
126       /* We don't have any other property... */
127       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
128       break;
129     }
130 }
131
132 static void
133 xml_search_tool_get_property (GObject    *object,
134                               guint       property_id,
135                               GValue     *value,
136                               GParamSpec *pspec)
137 {
138   VikXmlSearchTool *self = VIK_XML_SEARCH_TOOL (object);
139   VikXmlSearchToolPrivate *priv = XML_SEARCH_TOOL_GET_PRIVATE (self);
140
141   switch (property_id)
142     {
143     case PROP_URL_FORMAT:
144       g_value_set_string (value, priv->url_format);
145       break;
146
147     case PROP_LAT_PATH:
148       g_value_set_string (value, priv->lat_path);
149       break;
150
151     case PROP_LON_PATH:
152       g_value_set_string (value, priv->lon_path);
153       break;
154
155     default:
156       /* We don't have any other property... */
157       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
158       break;
159     }
160 }
161
162 static void vik_xml_search_tool_class_init ( VikXmlSearchToolClass *klass )
163 {
164   GObjectClass *object_class;
165   VikSearchToolClass *parent_class;
166   GParamSpec *pspec;
167
168   object_class = G_OBJECT_CLASS (klass);
169
170   object_class->finalize = vik_xml_search_tool_finalize;
171   object_class->set_property = xml_search_tool_set_property;
172   object_class->get_property = xml_search_tool_get_property;
173
174
175   pspec = g_param_spec_string ("url-format",
176                                "URL format",
177                                "The format of the URL",
178                                "<no-set>" /* default value */,
179                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
180   g_object_class_install_property (object_class,
181                                    PROP_URL_FORMAT,
182                                    pspec);
183
184   pspec = g_param_spec_string ("lat-path",
185                                "Lat path",
186                                "XPath of the latitude",
187                                "<no-set>" /* default value */,
188                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
189   g_object_class_install_property (object_class,
190                                    PROP_LAT_PATH,
191                                    pspec);
192
193   pspec = g_param_spec_string ("lon-path",
194                                "Lon path",
195                                "XPath of the longitude",
196                                "<no-set>" /* default value */,
197                                G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE);
198   g_object_class_install_property (object_class,
199                                    PROP_LON_PATH,
200                                    pspec);
201
202   parent_class = VIK_SEARCH_TOOL_CLASS (klass);
203
204   parent_class->get_coord = vik_xml_search_tool_get_coord;
205
206   g_type_class_add_private (klass, sizeof (VikXmlSearchToolPrivate));
207 }
208
209 VikXmlSearchTool *vik_xml_search_tool_new ()
210 {
211   return VIK_XML_SEARCH_TOOL ( g_object_new ( VIK_XML_SEARCH_TOOL_TYPE, "label", "Google", NULL ) );
212 }
213
214 static void vik_xml_search_tool_init ( VikXmlSearchTool *self )
215 {
216   VikXmlSearchToolPrivate *priv = XML_SEARCH_TOOL_GET_PRIVATE (self);
217   priv->url_format = NULL;
218   priv->lat_path = NULL;
219   priv->lon_path = NULL;
220   // 
221   priv->ll.lat = NAN;
222   priv->ll.lon = NAN;
223 }
224
225 static void vik_xml_search_tool_finalize ( GObject *gob )
226 {
227   G_OBJECT_GET_CLASS(gob)->finalize(gob);
228 }
229
230 static gboolean
231 stack_is_path (const GSList *stack,
232                const gchar  *path)
233 {
234   gboolean equal = TRUE;
235   int stack_len = g_list_length(stack);
236   int i = 0;
237   i = stack_len - 1;
238   while (equal == TRUE && i >= 0)
239   {
240     if (*path != '/')
241       equal = FALSE;
242     else
243       path++;
244     const gchar *current = g_list_nth_data(stack, i);
245     size_t len = strlen(current);
246     if (strncmp(path, current, len) != 0 )
247       equal = FALSE;
248     else
249     {
250       path += len;
251     }
252     i--;
253   }
254   if (*path != '\0')
255     equal = FALSE;
256   return equal;
257 }
258
259 /* Called for character data */
260 /* text is not nul-terminated */
261 static void
262 _text (GMarkupParseContext *context,
263        const gchar         *text,
264        gsize                text_len,  
265        gpointer             user_data,
266        GError             **error)
267 {
268   VikXmlSearchTool *self = VIK_XML_SEARCH_TOOL (user_data);
269   VikXmlSearchToolPrivate *priv = XML_SEARCH_TOOL_GET_PRIVATE (self);
270   const GSList *stack = g_markup_parse_context_get_element_stack (context);
271   gchar *textl = g_strndup(text, text_len);
272         if (stack_is_path (stack, priv->lat_path))
273         {
274     priv->ll.lat = g_ascii_strtod(textl, NULL);
275         }
276         if (stack_is_path (stack, priv->lon_path))
277         {
278     priv->ll.lon = g_ascii_strtod(textl, NULL);
279         }
280   g_free(textl);
281 }
282
283 static gboolean
284 parse_file_for_latlon(VikXmlSearchTool *self, gchar *filename, struct LatLon *ll)
285 {
286         GMarkupParser xml_parser;
287         GMarkupParseContext *xml_context;
288         GError *error;
289         VikXmlSearchToolPrivate *priv = XML_SEARCH_TOOL_GET_PRIVATE (self);
290
291         FILE *file = g_fopen (filename, "r");
292         if (file == NULL)
293                 /* TODO emit warning */
294                 return FALSE;
295         
296         /* setup context parse (ie callbacks) */
297         xml_parser.start_element = NULL;
298         xml_parser.end_element = NULL;
299         xml_parser.text = &_text;
300         xml_parser.passthrough = NULL;
301         xml_parser.error = NULL;
302         
303         xml_context = g_markup_parse_context_new(&xml_parser, 0, self, NULL);
304
305         /* setup result */
306         priv->ll.lat = NAN;
307         priv->ll.lon = NAN;
308         
309         gchar buff[BUFSIZ];
310         size_t nb;
311         while ((nb = fread (buff, sizeof(gchar), BUFSIZ, file)) > 0)
312         {
313                 if (!g_markup_parse_context_parse(xml_context, buff, nb, &error))
314                         printf("read_xml() : parsing error.\n");
315         }
316         /* cleanup */
317         if (!g_markup_parse_context_end_parse(xml_context, &error))
318                 printf("read_xml() : errors occurred reading file.\n");
319         
320         g_markup_parse_context_free(xml_context);
321         fclose (file);
322   
323   if (ll != NULL)
324   {
325     *ll = priv->ll;
326   }
327   
328   if (isnan(priv->ll.lat) || isnan(priv->ll.lat))
329                 /* At least one coordinate not found */
330                 return FALSE;
331         else
332                 return TRUE;
333 }
334
335 static int vik_xml_search_tool_get_coord ( VikSearchTool *object, VikWindow *vw, VikViewport *vvp, gchar *srch_str, VikCoord *coord )
336 {
337   FILE *tmp_file;
338   int tmp_fd;
339   gchar *tmpname;
340   gchar *uri;
341   gchar *escaped_srch_str;
342   int ret = 0;  /* OK */
343   struct LatLon ll;
344
345   g_debug("%s: raw search: %s", __FUNCTION__, srch_str);
346
347   escaped_srch_str = uri_escape(srch_str);
348
349   g_debug("%s: escaped search: %s", __FUNCTION__, escaped_srch_str);
350
351   if ((tmp_fd = g_file_open_tmp ("vikxmlsearch.XXXXXX", &tmpname, NULL)) == -1) {
352     g_critical(_("couldn't open temp file"));
353     exit(1);
354   }
355   
356   VikXmlSearchTool *self = VIK_XML_SEARCH_TOOL (object);
357   VikXmlSearchToolPrivate *priv = XML_SEARCH_TOOL_GET_PRIVATE (self);
358
359   tmp_file = fdopen(tmp_fd, "r+");
360   uri = g_strdup_printf(priv->url_format, escaped_srch_str);
361
362   /* TODO: curl may not be available */
363   if (curl_download_uri(uri, tmp_file, NULL)) {  /* error */
364     fclose(tmp_file);
365     tmp_file = NULL;
366     ret = -1;
367     goto done;
368   }
369
370   fclose(tmp_file);
371   tmp_file = NULL;
372   g_debug("%s: %s", __FILE__, tmpname);
373   if (!parse_file_for_latlon(self, tmpname, &ll)) {
374     ret = -1;
375     goto done;
376   }
377   vik_coord_load_from_latlon ( coord, vik_viewport_get_coord_mode(vvp), &ll );
378
379 done:
380   g_free(escaped_srch_str);
381   g_free(uri);
382   g_remove(tmpname);
383   g_free(tmpname);
384   return ret;
385 }