2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20 * Created by Quy Tonthat <qtonthat@gmail.com>
29 #include <glib/gstdio.h>
30 #include <glib/gprintf.h>
31 #include <glib/gi18n.h>
35 #include "curl_download.h"
37 #define GEONAMES_WIKIPEDIA_URL_FMT "http://ws.geonames.org/wikipediaBoundingBoxJSON?formatted=true&north=%s&south=%s&east=%s&west=%s"
38 #define GEONAMES_SEARCH_URL_FMT "http://ws.geonames.org/searchJSON?formatted=true&style=medium&maxRows=10&lang=en&q=%s"
39 #define GEONAMES_COUNTRY_PATTERN "\"countryName\": \""
40 #define GEONAMES_LONGITUDE_PATTERN "\"lng\": "
41 #define GEONAMES_NAME_PATTERN "\"name\": \""
42 #define GEONAMES_LATITUDE_PATTERN "\"lat\": "
43 #define GEONAMES_TITLE_PATTERN "\"title\": \""
44 #define GEONAMES_WIKIPEDIAURL_PATTERN "\"wikipediaUrl\": \""
45 #define GEONAMES_THUMBNAILIMG_PATTERN "\"thumbnailImg\": \""
46 #define GEONAMES_SEARCH_NOT_FOUND "not understand the location"
48 /* found_geoname: Type to contain data returned from GeoNames.org */
57 found_geoname
*new_found_geoname()
61 ret
= (found_geoname
*)g_malloc(sizeof(found_geoname
));
70 found_geoname
*copy_found_geoname(found_geoname
*src
)
72 found_geoname
*dest
= new_found_geoname();
73 dest
->name
= g_strdup(src
->name
);
74 dest
->country
= g_strdup(src
->country
);
75 dest
->ll
.lat
= src
->ll
.lat
;
76 dest
->ll
.lon
= src
->ll
.lon
;
77 dest
->desc
= g_strdup(src
->desc
);
81 static void free_list_geonames(found_geoname
*geoname
, gpointer userdata
)
83 g_free(geoname
->name
);
84 g_free(geoname
->country
);
85 g_free(geoname
->desc
);
88 void free_geoname_list(GList
*found_places
)
90 g_list_foreach(found_places
, (GFunc
)free_list_geonames
, NULL
);
91 g_list_free(found_places
);
94 static void none_found(VikWindow
*vw
)
96 GtkWidget
*dialog
= NULL
;
98 dialog
= gtk_dialog_new_with_buttons ( "", GTK_WINDOW(vw
), 0, GTK_STOCK_OK
, GTK_RESPONSE_ACCEPT
, NULL
);
99 gtk_window_set_title(GTK_WINDOW(dialog
), _("Search"));
101 GtkWidget
*search_label
= gtk_label_new(_("No entries found!"));
102 gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog
)->vbox
), search_label
, FALSE
, FALSE
, 5 );
103 gtk_widget_show_all(dialog
);
105 gtk_dialog_run ( GTK_DIALOG(dialog
) );
106 gtk_widget_destroy(dialog
);
109 void buttonToggled(GtkCellRendererToggle
* renderer
, gchar
* pathStr
, gpointer data
)
113 GtkTreePath
* path
= gtk_tree_path_new_from_string(pathStr
);
114 gtk_tree_model_get_iter(GTK_TREE_MODEL (data
), &iter
, path
);
115 gtk_tree_model_get(GTK_TREE_MODEL (data
), &iter
, 0, &enabled
, -1);
117 gtk_tree_store_set(GTK_TREE_STORE (data
), &iter
, 0, enabled
, -1);
120 GList
*a_select_geoname_from_list(GtkWindow
*parent
, GList
*geonames
, gboolean multiple_selection_allowed
, const gchar
*title
, const gchar
*msg
)
123 GtkCellRenderer
*renderer
;
124 GtkCellRenderer
*toggle_render
;
126 found_geoname
*geoname
;
127 gchar
*latlon_string
;
132 GtkWidget
*dialog
= gtk_dialog_new_with_buttons (title
,
134 GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
,
140 GtkWidget
*label
= gtk_label_new ( msg
);
142 if (multiple_selection_allowed
)
144 store
= gtk_tree_store_new(4, G_TYPE_BOOLEAN
, G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_STRING
);
148 store
= gtk_tree_store_new(3, G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_STRING
);
150 GList
*geoname_runner
= geonames
;
151 while (geoname_runner
)
153 geoname
= (found_geoname
*)geoname_runner
->data
;
154 latlon_string
= g_strdup_printf("(%f,%f)", geoname
->ll
.lat
, geoname
->ll
.lon
);
155 gtk_tree_store_append(store
, &iter
, NULL
);
156 if (multiple_selection_allowed
)
158 gtk_tree_store_set(store
, &iter
, 0, FALSE
, 1, geoname
->name
, 2, geoname
->country
, 3, latlon_string
, -1);
162 gtk_tree_store_set(store
, &iter
, 0, geoname
->name
, 1, geoname
->country
, 2, latlon_string
, -1);
164 geoname_runner
= g_list_next(geoname_runner
);
165 g_free(latlon_string
);
167 view
= gtk_tree_view_new();
168 renderer
= gtk_cell_renderer_text_new();
170 if (multiple_selection_allowed
)
172 toggle_render
= gtk_cell_renderer_toggle_new();
173 g_object_set(toggle_render
, "activatable", TRUE
, NULL
);
174 g_signal_connect(toggle_render
, "toggled", (GCallback
) buttonToggled
, GTK_TREE_MODEL(store
));
175 gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW(view
), -1, "Select", toggle_render
, "active", column_runner
, NULL
);
178 gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW(view
), -1, "Name", renderer
, "text", column_runner
, NULL
);
180 gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW(view
), -1, "Country", renderer
, "text", column_runner
, NULL
);
182 gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW(view
), -1, "Lat/Lon", renderer
, "text", column_runner
, NULL
);
183 gtk_tree_view_set_headers_visible( GTK_TREE_VIEW(view
), TRUE
);
184 gtk_tree_view_set_model(GTK_TREE_VIEW(view
), GTK_TREE_MODEL(store
));
185 gtk_tree_selection_set_mode( gtk_tree_view_get_selection(GTK_TREE_VIEW(view
)),
186 multiple_selection_allowed
? GTK_SELECTION_MULTIPLE
: GTK_SELECTION_BROWSE
);
187 g_object_unref(store
);
189 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog
)->vbox
), label
, FALSE
, FALSE
, 0);
190 gtk_widget_show ( label
);
191 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog
)->vbox
), view
, FALSE
, FALSE
, 0);
192 gtk_widget_show ( view
);
193 while ( gtk_dialog_run (GTK_DIALOG (dialog
)) == GTK_RESPONSE_ACCEPT
)
195 GtkTreeSelection
*selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(view
));
196 GList
*selected_geonames
= NULL
;
198 gtk_tree_model_get_iter_first( GTK_TREE_MODEL(store
), &iter
);
199 geoname_runner
= geonames
;
200 while (geoname_runner
)
203 if (multiple_selection_allowed
)
205 gtk_tree_model_get(GTK_TREE_MODEL(store
), &iter
, 0, &checked
, -1);
212 if (gtk_tree_selection_iter_is_selected(selection
, &iter
))
218 found_geoname
*copied
= copy_found_geoname(geoname_runner
->data
);
219 selected_geonames
= g_list_prepend(selected_geonames
, copied
);
221 geoname_runner
= g_list_next(geoname_runner
);
222 gtk_tree_model_iter_next(GTK_TREE_MODEL(store
), &iter
);
224 if (selected_geonames
)
226 gtk_widget_destroy ( dialog
);
227 return (selected_geonames
);
229 a_dialog_error_msg(parent
, _("Nothing was selected"));
231 gtk_widget_destroy ( dialog
);
235 GList
*get_entries_from_file(gchar
*file_name
)
240 gboolean more
= TRUE
;
241 gchar lat_buf
[32], lon_buf
[32];
244 GList
*found_places
= NULL
;
245 found_geoname
*geoname
= NULL
;
246 gchar
**found_entries
;
249 gchar
*wikipedia_url
= NULL
;
250 gchar
*thumbnail_url
= NULL
;
252 lat_buf
[0] = lon_buf
[0] = '\0';
254 if ((mf
= g_mapped_file_new(file_name
, FALSE
, NULL
)) == NULL
) {
255 g_critical(_("couldn't map temp file"));
258 len
= g_mapped_file_get_length(mf
);
259 text
= g_mapped_file_get_contents(mf
);
261 if (g_strstr_len(text
, len
, GEONAMES_SEARCH_NOT_FOUND
) != NULL
) {
264 found_entries
= g_strsplit(text
, "},", 0);
266 entry
= found_entries
[entry_runner
];
270 geoname
= new_found_geoname();
271 if ((pat
= g_strstr_len(entry
, strlen(entry
), GEONAMES_COUNTRY_PATTERN
))) {
272 pat
+= strlen(GEONAMES_COUNTRY_PATTERN
);
275 while (*pat
!= '"') {
279 geoname
-> country
= g_strndup(s
, fragment_len
);
281 if ((pat
= g_strstr_len(entry
, strlen(entry
), GEONAMES_LONGITUDE_PATTERN
)) == NULL
) {
285 pat
+= strlen(GEONAMES_LONGITUDE_PATTERN
);
289 while ((s
< (lon_buf
+ sizeof(lon_buf
))) && (pat
< (text
+ len
)) &&
290 (g_ascii_isdigit(*pat
) || (*pat
== '.')))
293 if ((pat
>= (text
+ len
)) || (lon_buf
[0] == '\0')) {
296 geoname
->ll
.lon
= g_ascii_strtod(lon_buf
, NULL
);
298 if ((pat
= g_strstr_len(entry
, strlen(entry
), GEONAMES_NAME_PATTERN
))) {
299 pat
+= strlen(GEONAMES_NAME_PATTERN
);
302 while (*pat
!= '"') {
306 geoname
-> name
= g_strndup(s
, fragment_len
);
308 if ((pat
= g_strstr_len(entry
, strlen(entry
), GEONAMES_TITLE_PATTERN
))) {
309 pat
+= strlen(GEONAMES_TITLE_PATTERN
);
312 while (*pat
!= '"') {
316 geoname
-> name
= g_strndup(s
, fragment_len
);
318 if ((pat
= g_strstr_len(entry
, strlen(entry
), GEONAMES_WIKIPEDIAURL_PATTERN
))) {
319 pat
+= strlen(GEONAMES_WIKIPEDIAURL_PATTERN
);
322 while (*pat
!= '"') {
326 wikipedia_url
= g_strndup(s
, fragment_len
);
328 if ((pat
= g_strstr_len(entry
, strlen(entry
), GEONAMES_THUMBNAILIMG_PATTERN
))) {
329 pat
+= strlen(GEONAMES_THUMBNAILIMG_PATTERN
);
332 while (*pat
!= '"') {
336 thumbnail_url
= g_strndup(s
, fragment_len
);
338 if ((pat
= g_strstr_len(entry
, strlen(entry
), GEONAMES_LATITUDE_PATTERN
)) == NULL
) {
342 pat
+= strlen(GEONAMES_LATITUDE_PATTERN
);
346 while ((s
< (lat_buf
+ sizeof(lat_buf
))) && (pat
< (text
+ len
)) &&
347 (g_ascii_isdigit(*pat
) || (*pat
== '.')))
350 if ((pat
>= (text
+ len
)) || (lat_buf
[0] == '\0')) {
353 geoname
->ll
.lat
= g_ascii_strtod(lat_buf
, NULL
);
363 geoname
-> desc
= g_strdup_printf("<a href=\"http://%s\" target=\"_blank\"><img src=\"%s\" border=\"0\"/></a>", wikipedia_url
, thumbnail_url
);
366 geoname
-> desc
= g_strdup_printf("<a href=\"http://%s\" target=\"_blank\">%s</a>", wikipedia_url
, geoname
->name
);
370 g_free(wikipedia_url
);
371 wikipedia_url
= NULL
;
374 g_free(thumbnail_url
);
375 thumbnail_url
= NULL
;
377 found_places
= g_list_prepend(found_places
, geoname
);
380 entry
= found_entries
[entry_runner
];
382 g_strfreev(found_entries
);
383 found_places
= g_list_reverse(found_places
);
384 g_mapped_file_free(mf
);
385 return(found_places
);
389 gchar
*download_url(gchar
*uri
)
395 if ((tmp_fd
= g_file_open_tmp ("vikgsearch.XXXXXX", &tmpname
, NULL
)) == -1) {
396 g_critical(_("couldn't open temp file"));
399 tmp_file
= fdopen(tmp_fd
, "r+");
401 // TODO: curl may not be available
402 if (curl_download_uri(uri
, tmp_file
, NULL
)) { // error
414 void a_geonames_wikipedia_box(VikWindow
*vw
, VikTrwLayer
*vtl
, VikLayersPanel
*vlp
, struct LatLon maxmin
[2])
421 VikWaypoint
*wiki_wp
;
422 found_geoname
*wiki_geoname
;
424 /* encode doubles in a C locale */
425 gchar
*north
= a_coords_dtostr(maxmin
[0].lat
);
426 gchar
*south
= a_coords_dtostr(maxmin
[1].lat
);
427 gchar
*east
= a_coords_dtostr(maxmin
[0].lon
);
428 gchar
*west
= a_coords_dtostr(maxmin
[1].lon
);
429 uri
= g_strdup_printf(GEONAMES_WIKIPEDIA_URL_FMT
, north
, south
, east
, west
);
430 g_free(north
); north
= NULL
;
431 g_free(south
); south
= NULL
;
432 g_free(east
); east
= NULL
;
433 g_free(west
); west
= NULL
;
434 tmpname
= download_url(uri
);
439 wiki_places
= get_entries_from_file(tmpname
);
440 if (g_list_length(wiki_places
) == 0) {
444 selected
= a_select_geoname_from_list(VIK_GTK_WINDOW_FROM_WIDGET(vw
), wiki_places
, TRUE
, "Select articles", "Select the articles you want to add.");
445 wp_runner
= selected
;
447 wiki_geoname
= (found_geoname
*)wp_runner
->data
;
448 wiki_wp
= vik_waypoint_new();
449 wiki_wp
->visible
= TRUE
;
450 vik_coord_load_from_latlon(&(wiki_wp
->coord
), vik_trw_layer_get_coord_mode ( vtl
), &(wiki_geoname
->ll
));
451 vik_waypoint_set_comment(wiki_wp
, wiki_geoname
->desc
);
452 vik_trw_layer_filein_add_waypoint ( vtl
, wiki_geoname
->name
, wiki_wp
);
453 wp_runner
= g_list_next(wp_runner
);
455 free_geoname_list(wiki_places
);
456 free_geoname_list(selected
);
461 vik_layers_panel_emit_update(vlp
);