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_COUNTRY_PATTERN "\"countryName\": \""
39 #define GEONAMES_LONGITUDE_PATTERN "\"lng\": "
40 #define GEONAMES_NAME_PATTERN "\"name\": \""
41 #define GEONAMES_LATITUDE_PATTERN "\"lat\": "
42 #define GEONAMES_TITLE_PATTERN "\"title\": \""
43 #define GEONAMES_WIKIPEDIAURL_PATTERN "\"wikipediaUrl\": \""
44 #define GEONAMES_THUMBNAILIMG_PATTERN "\"thumbnailImg\": \""
45 #define GEONAMES_SEARCH_NOT_FOUND "not understand the location"
47 /* found_geoname: Type to contain data returned from GeoNames.org */
56 found_geoname
*new_found_geoname()
60 ret
= (found_geoname
*)g_malloc(sizeof(found_geoname
));
69 found_geoname
*copy_found_geoname(found_geoname
*src
)
71 found_geoname
*dest
= new_found_geoname();
72 dest
->name
= g_strdup(src
->name
);
73 dest
->country
= g_strdup(src
->country
);
74 dest
->ll
.lat
= src
->ll
.lat
;
75 dest
->ll
.lon
= src
->ll
.lon
;
76 dest
->desc
= g_strdup(src
->desc
);
80 static void free_list_geonames(found_geoname
*geoname
, gpointer userdata
)
82 g_free(geoname
->name
);
83 g_free(geoname
->country
);
84 g_free(geoname
->desc
);
87 void free_geoname_list(GList
*found_places
)
89 g_list_foreach(found_places
, (GFunc
)free_list_geonames
, NULL
);
90 g_list_free(found_places
);
93 static void none_found(VikWindow
*vw
)
95 GtkWidget
*dialog
= NULL
;
97 dialog
= gtk_dialog_new_with_buttons ( "", GTK_WINDOW(vw
), 0, GTK_STOCK_OK
, GTK_RESPONSE_ACCEPT
, NULL
);
98 gtk_window_set_title(GTK_WINDOW(dialog
), _("Search"));
100 GtkWidget
*search_label
= gtk_label_new(_("No entries found!"));
101 gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog
)->vbox
), search_label
, FALSE
, FALSE
, 5 );
102 gtk_widget_show_all(dialog
);
104 gtk_dialog_run ( GTK_DIALOG(dialog
) );
105 gtk_widget_destroy(dialog
);
108 void buttonToggled(GtkCellRendererToggle
* renderer
, gchar
* pathStr
, gpointer data
)
112 GtkTreePath
* path
= gtk_tree_path_new_from_string(pathStr
);
113 gtk_tree_model_get_iter(GTK_TREE_MODEL (data
), &iter
, path
);
114 gtk_tree_model_get(GTK_TREE_MODEL (data
), &iter
, 0, &enabled
, -1);
116 gtk_tree_store_set(GTK_TREE_STORE (data
), &iter
, 0, enabled
, -1);
119 GList
*a_select_geoname_from_list(GtkWindow
*parent
, GList
*geonames
, gboolean multiple_selection_allowed
, const gchar
*title
, const gchar
*msg
)
122 GtkCellRenderer
*renderer
;
123 GtkCellRenderer
*toggle_render
;
125 found_geoname
*geoname
;
126 gchar
*latlon_string
;
131 GtkWidget
*dialog
= gtk_dialog_new_with_buttons (title
,
133 GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
,
139 GtkWidget
*label
= gtk_label_new ( msg
);
141 if (multiple_selection_allowed
)
143 store
= gtk_tree_store_new(4, G_TYPE_BOOLEAN
, G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_STRING
);
147 store
= gtk_tree_store_new(3, G_TYPE_STRING
, G_TYPE_STRING
, G_TYPE_STRING
);
149 GList
*geoname_runner
= geonames
;
150 while (geoname_runner
)
152 geoname
= (found_geoname
*)geoname_runner
->data
;
153 latlon_string
= g_strdup_printf("(%f,%f)", geoname
->ll
.lat
, geoname
->ll
.lon
);
154 gtk_tree_store_append(store
, &iter
, NULL
);
155 if (multiple_selection_allowed
)
157 gtk_tree_store_set(store
, &iter
, 0, FALSE
, 1, geoname
->name
, 2, geoname
->country
, 3, latlon_string
, -1);
161 gtk_tree_store_set(store
, &iter
, 0, geoname
->name
, 1, geoname
->country
, 2, latlon_string
, -1);
163 geoname_runner
= g_list_next(geoname_runner
);
164 g_free(latlon_string
);
166 view
= gtk_tree_view_new();
167 renderer
= gtk_cell_renderer_text_new();
169 if (multiple_selection_allowed
)
171 toggle_render
= gtk_cell_renderer_toggle_new();
172 g_object_set(toggle_render
, "activatable", TRUE
, NULL
);
173 g_signal_connect(toggle_render
, "toggled", (GCallback
) buttonToggled
, GTK_TREE_MODEL(store
));
174 gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW(view
), -1, "Select", toggle_render
, "active", column_runner
, NULL
);
177 gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW(view
), -1, "Name", renderer
, "text", column_runner
, NULL
);
179 gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW(view
), -1, "Country", renderer
, "text", column_runner
, NULL
);
181 gtk_tree_view_insert_column_with_attributes( GTK_TREE_VIEW(view
), -1, "Lat/Lon", renderer
, "text", column_runner
, NULL
);
182 gtk_tree_view_set_headers_visible( GTK_TREE_VIEW(view
), TRUE
);
183 gtk_tree_view_set_model(GTK_TREE_VIEW(view
), GTK_TREE_MODEL(store
));
184 gtk_tree_selection_set_mode( gtk_tree_view_get_selection(GTK_TREE_VIEW(view
)),
185 multiple_selection_allowed
? GTK_SELECTION_MULTIPLE
: GTK_SELECTION_BROWSE
);
186 g_object_unref(store
);
188 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog
)->vbox
), label
, FALSE
, FALSE
, 0);
189 gtk_widget_show ( label
);
190 gtk_box_pack_start (GTK_BOX(GTK_DIALOG(dialog
)->vbox
), view
, FALSE
, FALSE
, 0);
191 gtk_widget_show ( view
);
192 while ( gtk_dialog_run (GTK_DIALOG (dialog
)) == GTK_RESPONSE_ACCEPT
)
194 GtkTreeSelection
*selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(view
));
195 GList
*selected_geonames
= NULL
;
197 gtk_tree_model_get_iter_first( GTK_TREE_MODEL(store
), &iter
);
198 geoname_runner
= geonames
;
199 while (geoname_runner
)
202 if (multiple_selection_allowed
)
204 gtk_tree_model_get(GTK_TREE_MODEL(store
), &iter
, 0, &checked
, -1);
211 if (gtk_tree_selection_iter_is_selected(selection
, &iter
))
217 found_geoname
*copied
= copy_found_geoname(geoname_runner
->data
);
218 selected_geonames
= g_list_prepend(selected_geonames
, copied
);
220 geoname_runner
= g_list_next(geoname_runner
);
221 gtk_tree_model_iter_next(GTK_TREE_MODEL(store
), &iter
);
223 if (selected_geonames
)
225 gtk_widget_destroy ( dialog
);
226 return (selected_geonames
);
228 a_dialog_error_msg(parent
, _("Nothing was selected"));
230 gtk_widget_destroy ( dialog
);
234 GList
*get_entries_from_file(gchar
*file_name
)
239 gboolean more
= TRUE
;
240 gchar lat_buf
[32], lon_buf
[32];
243 GList
*found_places
= NULL
;
244 found_geoname
*geoname
= NULL
;
245 gchar
**found_entries
;
248 gchar
*wikipedia_url
= NULL
;
249 gchar
*thumbnail_url
= NULL
;
251 lat_buf
[0] = lon_buf
[0] = '\0';
253 if ((mf
= g_mapped_file_new(file_name
, FALSE
, NULL
)) == NULL
) {
254 g_critical(_("couldn't map temp file"));
257 len
= g_mapped_file_get_length(mf
);
258 text
= g_mapped_file_get_contents(mf
);
260 if (g_strstr_len(text
, len
, GEONAMES_SEARCH_NOT_FOUND
) != NULL
) {
263 found_entries
= g_strsplit(text
, "},", 0);
265 entry
= found_entries
[entry_runner
];
269 geoname
= new_found_geoname();
270 if ((pat
= g_strstr_len(entry
, strlen(entry
), GEONAMES_COUNTRY_PATTERN
))) {
271 pat
+= strlen(GEONAMES_COUNTRY_PATTERN
);
274 while (*pat
!= '"') {
278 geoname
-> country
= g_strndup(s
, fragment_len
);
280 if ((pat
= g_strstr_len(entry
, strlen(entry
), GEONAMES_LONGITUDE_PATTERN
)) == NULL
) {
284 pat
+= strlen(GEONAMES_LONGITUDE_PATTERN
);
288 while ((s
< (lon_buf
+ sizeof(lon_buf
))) && (pat
< (text
+ len
)) &&
289 (g_ascii_isdigit(*pat
) || (*pat
== '.')))
292 if ((pat
>= (text
+ len
)) || (lon_buf
[0] == '\0')) {
295 geoname
->ll
.lon
= g_ascii_strtod(lon_buf
, NULL
);
297 if ((pat
= g_strstr_len(entry
, strlen(entry
), GEONAMES_NAME_PATTERN
))) {
298 pat
+= strlen(GEONAMES_NAME_PATTERN
);
301 while (*pat
!= '"') {
305 geoname
-> name
= g_strndup(s
, fragment_len
);
307 if ((pat
= g_strstr_len(entry
, strlen(entry
), GEONAMES_TITLE_PATTERN
))) {
308 pat
+= strlen(GEONAMES_TITLE_PATTERN
);
311 while (*pat
!= '"') {
315 geoname
-> name
= g_strndup(s
, fragment_len
);
317 if ((pat
= g_strstr_len(entry
, strlen(entry
), GEONAMES_WIKIPEDIAURL_PATTERN
))) {
318 pat
+= strlen(GEONAMES_WIKIPEDIAURL_PATTERN
);
321 while (*pat
!= '"') {
325 wikipedia_url
= g_strndup(s
, fragment_len
);
327 if ((pat
= g_strstr_len(entry
, strlen(entry
), GEONAMES_THUMBNAILIMG_PATTERN
))) {
328 pat
+= strlen(GEONAMES_THUMBNAILIMG_PATTERN
);
331 while (*pat
!= '"') {
335 thumbnail_url
= g_strndup(s
, fragment_len
);
337 if ((pat
= g_strstr_len(entry
, strlen(entry
), GEONAMES_LATITUDE_PATTERN
)) == NULL
) {
341 pat
+= strlen(GEONAMES_LATITUDE_PATTERN
);
345 while ((s
< (lat_buf
+ sizeof(lat_buf
))) && (pat
< (text
+ len
)) &&
346 (g_ascii_isdigit(*pat
) || (*pat
== '.')))
349 if ((pat
>= (text
+ len
)) || (lat_buf
[0] == '\0')) {
352 geoname
->ll
.lat
= g_ascii_strtod(lat_buf
, NULL
);
362 geoname
-> desc
= g_strdup_printf("<a href=\"http://%s\" target=\"_blank\"><img src=\"%s\" border=\"0\"/></a>", wikipedia_url
, thumbnail_url
);
365 geoname
-> desc
= g_strdup_printf("<a href=\"http://%s\" target=\"_blank\">%s</a>", wikipedia_url
, geoname
->name
);
369 g_free(wikipedia_url
);
370 wikipedia_url
= NULL
;
373 g_free(thumbnail_url
);
374 thumbnail_url
= NULL
;
376 found_places
= g_list_prepend(found_places
, geoname
);
379 entry
= found_entries
[entry_runner
];
381 g_strfreev(found_entries
);
382 found_places
= g_list_reverse(found_places
);
383 g_mapped_file_free(mf
);
384 return(found_places
);
388 gchar
*download_url(gchar
*uri
)
394 if ((tmp_fd
= g_file_open_tmp ("vikgsearch.XXXXXX", &tmpname
, NULL
)) == -1) {
395 g_critical(_("couldn't open temp file"));
398 tmp_file
= fdopen(tmp_fd
, "r+");
400 // TODO: curl may not be available
401 if (curl_download_uri(uri
, tmp_file
, NULL
, 0)) { // error
413 void a_geonames_wikipedia_box(VikWindow
*vw
, VikTrwLayer
*vtl
, VikLayersPanel
*vlp
, struct LatLon maxmin
[2])
420 VikWaypoint
*wiki_wp
;
421 found_geoname
*wiki_geoname
;
423 /* encode doubles in a C locale */
424 gchar
*north
= a_coords_dtostr(maxmin
[0].lat
);
425 gchar
*south
= a_coords_dtostr(maxmin
[1].lat
);
426 gchar
*east
= a_coords_dtostr(maxmin
[0].lon
);
427 gchar
*west
= a_coords_dtostr(maxmin
[1].lon
);
428 uri
= g_strdup_printf(GEONAMES_WIKIPEDIA_URL_FMT
, north
, south
, east
, west
);
429 g_free(north
); north
= NULL
;
430 g_free(south
); south
= NULL
;
431 g_free(east
); east
= NULL
;
432 g_free(west
); west
= NULL
;
433 tmpname
= download_url(uri
);
438 wiki_places
= get_entries_from_file(tmpname
);
439 if (g_list_length(wiki_places
) == 0) {
443 selected
= a_select_geoname_from_list(VIK_GTK_WINDOW_FROM_WIDGET(vw
), wiki_places
, TRUE
, "Select articles", "Select the articles you want to add.");
444 wp_runner
= selected
;
446 wiki_geoname
= (found_geoname
*)wp_runner
->data
;
447 wiki_wp
= vik_waypoint_new();
448 wiki_wp
->visible
= TRUE
;
449 vik_coord_load_from_latlon(&(wiki_wp
->coord
), vik_trw_layer_get_coord_mode ( vtl
), &(wiki_geoname
->ll
));
450 vik_waypoint_set_comment(wiki_wp
, wiki_geoname
->desc
);
451 vik_trw_layer_filein_add_waypoint ( vtl
, wiki_geoname
->name
, wiki_wp
);
452 wp_runner
= g_list_next(wp_runner
);
454 free_geoname_list(wiki_places
);
455 free_geoname_list(selected
);
460 vik_layers_panel_emit_update(vlp
);