Contents of /trunk/src/wms.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 2 - (show annotations)
Wed Dec 10 00:00:05 2008 UTC (15 years, 5 months ago) by achadwick
File MIME type: text/plain
File size: 29906 byte(s)
Begin trunk. No code changes.
1 /*
2 * Copyright (C) 2008 Till Harbaum <till@harbaum.org>.
3 *
4 * This file is part of OSM2Go.
5 *
6 * OSM2Go 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 3 of the License, or
9 * (at your option) any later version.
10 *
11 * OSM2Go 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.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with OSM2Go. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "appdata.h"
21
22 #include <curl/curl.h>
23 #include <curl/types.h> /* new for v7 */
24 #include <curl/easy.h> /* new for v7 */
25
26 #include <libxml/parser.h>
27 #include <libxml/tree.h>
28
29 #ifndef LIBXML_TREE_ENABLED
30 #error "Tree not enabled in libxml"
31 #endif
32
33 #define WMS_FORMAT_JPG (1<<0)
34 #define WMS_FORMAT_JPEG (1<<1)
35 #define WMS_FORMAT_PNG (1<<2)
36 #define WMS_FORMAT_GIF (1<<3)
37
38 typedef struct {
39 pos_t min, max;
40 gboolean valid;
41 } wms_llbbox_t;
42
43 typedef struct wms_layer_s {
44 char *title;
45 char *name;
46 gboolean epsg4326, selected;
47 wms_llbbox_t llbbox;
48
49 struct wms_layer_s *children;
50 struct wms_layer_s *next;
51 } wms_layer_t;
52
53 typedef struct {
54 gulong format;
55 } wms_getmap_t;
56
57 typedef struct {
58 wms_getmap_t *getmap;
59 } wms_request_t;
60
61 typedef struct {
62 char *title;
63 } wms_service_t;
64
65 typedef struct {
66 wms_layer_t *layer;
67 wms_request_t *request;
68 } wms_cap_t;
69
70 typedef struct {
71 char *server;
72 char *path;
73 gint width, height;
74
75 wms_service_t *service;
76 wms_cap_t *cap;
77 } wms_t;
78
79 typedef struct {
80 char *ptr;
81 int len;
82 } curl_data_t;
83
84 static size_t write_callback(void *ptr, size_t size, size_t nmemb,
85 void *stream) {
86 curl_data_t *p = (curl_data_t*)stream;
87
88 p->ptr = g_realloc(p->ptr, p->len + size*nmemb + 1);
89 if(p->ptr) {
90 memcpy(p->ptr+p->len, ptr, size*nmemb);
91 p->len += size*nmemb;
92 p->ptr[p->len] = 0;
93 }
94 return nmemb;
95 }
96
97 static char *wms_download(char *url, char *filename) {
98 CURL *curl;
99 CURLcode res;
100 char buffer[CURL_ERROR_SIZE];
101 curl_data_t write_data;
102 FILE *write_file = NULL;
103
104 if(filename) {
105 write_file = fopen(filename, "w");
106 } else {
107 write_data.ptr = NULL;
108 write_data.len = 0;
109 }
110
111 printf("download of \"%s\"\n", url);
112
113 curl = curl_easy_init();
114 if(curl) {
115 curl_easy_setopt(curl, CURLOPT_URL, url);
116 curl_easy_setopt(curl, CURLOPT_ERRORBUFFER, buffer);
117
118 if(filename) {
119 curl_easy_setopt(curl, CURLOPT_WRITEDATA, write_file);
120 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, fwrite);
121 } else {
122 curl_easy_setopt(curl, CURLOPT_WRITEDATA, &write_data);
123 curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, write_callback);
124 }
125
126 res = curl_easy_perform(curl);
127
128 long response;
129 curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &response);
130
131 curl_easy_cleanup(curl);
132
133 if(filename) {
134 /* just a flag that everything went well */
135 write_data.ptr = (char*)TRUE;
136 fclose(write_file);
137 }
138
139 /* erase reply buffer/file if there was an error */
140 if((res != 0) || (response != 200)) {
141 if(filename)
142 g_remove(filename);
143 else
144 g_free(write_data.ptr);
145
146 write_data.ptr = NULL;
147 }
148 }
149
150 return write_data.ptr;
151 }
152
153 gboolean xmlTextIs(xmlDocPtr doc, xmlNodePtr list, char *str) {
154 char *nstr = (char*)xmlNodeListGetString(doc, list, 1);
155 if(!nstr) return FALSE;
156
157 printf("text = %s\n", nstr);
158
159 gboolean match = (strcmp(str, nstr) == 0);
160 xmlFree(nstr);
161 return match;
162 }
163
164 gboolean xmlPropIs(xmlNode *node, char *prop, char *str) {
165 char *prop_str = (char*)xmlGetProp(node, BAD_CAST prop);
166 if(!prop_str) return FALSE;
167
168 gboolean match = (strcmp(prop_str, str) == 0);
169 xmlFree(prop_str);
170 return match;
171 }
172
173 float xmlGetPropFloat(xmlNode *node, char *prop) {
174 char *prop_str = (char*)xmlGetProp(node, BAD_CAST prop);
175 if(!prop_str) return NAN;
176
177 float retval = g_ascii_strtod(prop_str, NULL);
178 xmlFree(prop_str);
179 return retval;
180 }
181
182 static gboolean wms_bbox_is_valid(pos_t *min, pos_t *max) {
183 /* all four coordinates are valid? */
184 if(isnan(min->lat)||isnan(min->lon)||isnan(max->lat)||isnan(max->lon))
185 return FALSE;
186
187 /* min/max span a useful range? */
188 if(max->lat - min->lat < 1.0) return FALSE;
189 if(max->lon - min->lon < 1.0) return FALSE;
190
191 /* useful angles? */
192 if(min->lat > 90.0 || min->lat < -90.0) return FALSE;
193 if(max->lat > 90.0 || max->lat < -90.0) return FALSE;
194 if(min->lon > 180.0 || min->lon < -180.0) return FALSE;
195 if(max->lon > 180.0 || max->lon < -180.0) return FALSE;
196
197 return TRUE;
198 }
199
200 static wms_layer_t *wms_cap_parse_layer(xmlDocPtr doc, xmlNode *a_node) {
201 wms_layer_t *wms_layer = NULL;
202 xmlNode *cur_node = NULL;
203 char *str = NULL;
204
205 wms_layer = g_new0(wms_layer_t, 1);
206 wms_layer->llbbox.min.lon = wms_layer->llbbox.min.lat = NAN;
207 wms_layer->llbbox.max.lon = wms_layer->llbbox.max.lat = NAN;
208
209 wms_layer_t **children = &(wms_layer->children);
210
211 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
212 if (cur_node->type == XML_ELEMENT_NODE) {
213 if(strcasecmp((char*)cur_node->name, "Layer") == 0) {
214 *children = wms_cap_parse_layer(doc, cur_node);
215 if(*children) children = &((*children)->next);
216 } else if(strcasecmp((char*)cur_node->name, "Name") == 0) {
217 str = (char*)xmlNodeListGetString(doc, cur_node->children, 1);
218 wms_layer->name = g_strdup(str);
219 xmlFree(str);
220 } else if(strcasecmp((char*)cur_node->name, "Title") == 0) {
221 str = (char*)xmlNodeListGetString(doc, cur_node->children, 1);
222 wms_layer->title = g_strdup(str);
223 xmlFree(str);
224 } else if(strcasecmp((char*)cur_node->name, "SRS") == 0) {
225 if(xmlTextIs(doc, cur_node->children, "EPSG:4326"))
226 wms_layer->epsg4326 = TRUE;
227 } else if(strcasecmp((char*)cur_node->name, "LatLonBoundingBox") == 0) {
228 wms_layer->llbbox.min.lat = xmlGetPropFloat(cur_node, "miny");
229 wms_layer->llbbox.min.lon = xmlGetPropFloat(cur_node, "minx");
230 wms_layer->llbbox.max.lat = xmlGetPropFloat(cur_node, "maxy");
231 wms_layer->llbbox.max.lon = xmlGetPropFloat(cur_node, "maxx");
232 } else
233 printf("found unhandled WMT_MS_Capabilities/Capability/Layer/%s\n",
234 cur_node->name);
235 }
236 }
237
238 wms_layer->llbbox.valid = wms_bbox_is_valid(&wms_layer->llbbox.min,
239 &wms_layer->llbbox.max);
240
241 printf("------------------- Layer: %s ---------------------------\n",
242 wms_layer->title);
243 printf("Name: %s\n", wms_layer->name);
244 printf("EPSG-4326: %s\n", wms_layer->epsg4326?"yes":"no");
245 if(wms_layer->llbbox.valid)
246 printf("LatLonBBox: %f/%f %f/%f\n",
247 wms_layer->llbbox.min.lat, wms_layer->llbbox.min.lon,
248 wms_layer->llbbox.max.lat, wms_layer->llbbox.max.lon);
249 else
250 printf("No/invalid LatLonBBox\n");
251
252 return wms_layer;
253 }
254
255 static wms_getmap_t *wms_cap_parse_getmap(xmlDocPtr doc, xmlNode *a_node) {
256 wms_getmap_t *wms_getmap = NULL;
257 xmlNode *cur_node = NULL;
258
259 wms_getmap = g_new0(wms_getmap_t, 1);
260
261 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
262 if (cur_node->type == XML_ELEMENT_NODE) {
263 if(strcasecmp((char*)cur_node->name, "Format") == 0) {
264 if(xmlTextIs(doc, cur_node->children, "image/png"))
265 wms_getmap->format |= WMS_FORMAT_PNG;
266 if(xmlTextIs(doc, cur_node->children, "image/gif"))
267 wms_getmap->format |= WMS_FORMAT_GIF;
268 if(xmlTextIs(doc, cur_node->children, "image/jpg"))
269 wms_getmap->format |= WMS_FORMAT_JPG;
270 if(xmlTextIs(doc, cur_node->children, "image/jpeg"))
271 wms_getmap->format |= WMS_FORMAT_JPEG;
272 } else
273 printf("found unhandled "
274 "WMT_MS_Capabilities/Capability/Request/GetMap/%s\n",
275 cur_node->name);
276 }
277 }
278
279 printf("Supported formats: %s%s%s\n",
280 (wms_getmap->format & WMS_FORMAT_PNG)?"png ":"",
281 (wms_getmap->format & WMS_FORMAT_GIF)?"gif ":"",
282 (wms_getmap->format & (WMS_FORMAT_JPG | WMS_FORMAT_JPEG))?"jpg ":"");
283 return wms_getmap;
284 }
285
286 static wms_request_t *wms_cap_parse_request(xmlDocPtr doc, xmlNode *a_node) {
287 wms_request_t *wms_request = NULL;
288 xmlNode *cur_node = NULL;
289
290 wms_request = g_new0(wms_request_t, 1);
291
292 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
293 if (cur_node->type == XML_ELEMENT_NODE) {
294 if(strcasecmp((char*)cur_node->name, "GetMap") == 0) {
295 wms_request->getmap = wms_cap_parse_getmap(doc, cur_node);
296 } else
297 printf("found unhandled WMT_MS_Capabilities/Capability/Request/%s\n",
298 cur_node->name);
299 }
300 }
301
302 return wms_request;
303 }
304
305 static wms_cap_t *wms_cap_parse_cap(xmlDocPtr doc, xmlNode *a_node) {
306 wms_cap_t *wms_cap = NULL;
307 xmlNode *cur_node = NULL;
308
309 wms_cap = g_new0(wms_cap_t, 1);
310 wms_layer_t **layer = &(wms_cap->layer);
311
312 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
313 if (cur_node->type == XML_ELEMENT_NODE) {
314 if(strcasecmp((char*)cur_node->name, "Request") == 0) {
315 wms_cap->request = wms_cap_parse_request(doc, cur_node);
316 } else if(strcasecmp((char*)cur_node->name, "Layer") == 0) {
317 *layer = wms_cap_parse_layer(doc, cur_node);
318 if(*layer) layer = &((*layer)->next);
319 } else
320 printf("found unhandled WMT_MS_Capabilities/Capability/%s\n",
321 cur_node->name);
322 }
323 }
324
325 return wms_cap;
326 }
327
328 static wms_service_t *wms_cap_parse_service(xmlDocPtr doc, xmlNode *a_node) {
329 wms_service_t *wms_service = NULL;
330 xmlNode *cur_node = NULL;
331 char *str = NULL;
332
333 wms_service = g_new0(wms_service_t, 1);
334
335 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
336 if (cur_node->type == XML_ELEMENT_NODE) {
337 if(strcasecmp((char*)cur_node->name, "title") == 0) {
338 str = (char*)xmlNodeListGetString(doc, cur_node->children, 1);
339 wms_service->title = g_strdup(str);
340 xmlFree(str);
341 } else
342 printf("found unhandled WMT_MS_Capabilities/Service/%s\n",
343 cur_node->name);
344 }
345 }
346
347 printf("-- Service --\n");
348 printf("Title: %s\n", wms_service->title);
349
350 return wms_service;
351 }
352
353 static void wms_cap_parse(wms_t *wms, xmlDocPtr doc, xmlNode *a_node) {
354 xmlNode *cur_node = NULL;
355
356 for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
357 if (cur_node->type == XML_ELEMENT_NODE) {
358
359 if(strcasecmp((char*)cur_node->name, "Service") == 0) {
360 if(!wms->service)
361 wms->service = wms_cap_parse_service(doc, cur_node);
362 } else if(strcasecmp((char*)cur_node->name, "Capability") == 0) {
363 if(!wms->cap)
364 wms->cap = wms_cap_parse_cap(doc, cur_node);
365 } else
366 printf("found unhandled WMT_MS_Capabilities/%s\n", cur_node->name);
367 }
368 }
369 }
370
371 /* parse root element */
372 static void wms_cap_parse_root(wms_t *wms, xmlDocPtr doc, xmlNode *a_node) {
373 xmlNode *cur_node = NULL;
374
375 for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
376 if (cur_node->type == XML_ELEMENT_NODE) {
377
378 if(strcasecmp((char*)cur_node->name, "WMT_MS_Capabilities") == 0) {
379 wms_cap_parse(wms, doc, cur_node);
380 } else
381 printf("found unhandled %s\n", cur_node->name);
382 }
383 }
384 }
385
386 static void wms_cap_parse_doc(wms_t *wms, xmlDocPtr doc) {
387 /* Get the root element node */
388 xmlNode *root_element = xmlDocGetRootElement(doc);
389
390 wms_cap_parse_root(wms, doc, root_element);
391
392 /*free the document */
393 xmlFreeDoc(doc);
394
395 /*
396 * Free the global variables that may
397 * have been allocated by the parser.
398 */
399 xmlCleanupParser();
400 }
401
402 /* get pixel extent of image display */
403 void wms_setup_extent(project_t *project, wms_t *wms) {
404 pos_t center;
405 lpos_t lcenter, lmin, lmax;
406 float scale;
407
408 center.lat = (project->min.lat + project->max.lat)/2;
409 center.lon = (project->min.lon + project->max.lon)/2;
410
411 pos2lpos_center(&center, &lcenter);
412
413 /* the scale is needed to accomodate for "streching" */
414 /* by the mercartor projection */
415 scale = cos(DEG2RAD(center.lat));
416
417 pos2lpos_center(&project->min, &lmin);
418 lmin.x -= lcenter.x;
419 lmin.y -= lcenter.y;
420 lmin.x *= scale;
421 lmin.y *= scale;
422
423 pos2lpos_center(&project->max, &lmax);
424 lmax.x -= lcenter.x;
425 lmax.y -= lcenter.y;
426 lmax.x *= scale;
427 lmax.y *= scale;
428
429 wms->width = lmax.x - lmin.x;
430 wms->height = lmax.y - lmin.y;
431
432 if(wms->width > 2048) wms->width = 2048;
433 if(wms->height > 2048) wms->height = 2048;
434
435 printf("WMS: required image size = %dx%d\n",
436 wms->width, wms->height);
437 }
438
439 /* --------------- freeing stuff ------------------- */
440
441 static void wms_layer_free(wms_layer_t *layer) {
442 while(layer) {
443
444 if(layer->title) g_free(layer->title);
445 if(layer->name) g_free(layer->name);
446
447 if(layer->children) wms_layer_free(layer->children);
448
449 wms_layer_t *next = layer->next;
450 g_free(layer);
451 layer = next;
452 }
453 }
454
455 static void wms_getmap_free(wms_getmap_t *getmap) {
456 g_free(getmap);
457 }
458
459 static void wms_request_free(wms_request_t *request) {
460 if(request->getmap) wms_getmap_free(request->getmap);
461 g_free(request);
462 }
463
464 static void wms_cap_free(wms_cap_t *cap) {
465 if(cap->layer) wms_layer_free(cap->layer);
466 if(cap->request) wms_request_free(cap->request);
467 g_free(cap);
468 }
469
470 static void wms_service_free(wms_service_t *service) {
471 if(service->title) g_free(service->title);
472 g_free(service);
473 }
474
475 static void wms_free(wms_t *wms) {
476 if(wms->server) g_free(wms->server);
477 if(wms->path) g_free(wms->path);
478 if(wms->cap) wms_cap_free(wms->cap);
479 if(wms->service) wms_service_free(wms->service);
480 g_free(wms);
481 }
482
483 /* ---------------------- use ------------------- */
484
485 static gboolean wms_llbbox_fits(project_t *project, wms_llbbox_t *llbbox) {
486 if(!llbbox || !llbbox->valid ||
487 (project->min.lat < llbbox->min.lat) ||
488 (project->min.lon < llbbox->min.lon) ||
489 (project->max.lat > llbbox->max.lat) ||
490 (project->max.lon > llbbox->max.lon))
491 return FALSE;
492
493 return TRUE;
494 }
495
496 static void wms_get_child_layers(wms_layer_t *layer,
497 gint depth, gboolean epsg4326, wms_llbbox_t *llbbox,
498 wms_layer_t **c_layer) {
499 while(layer) {
500
501 /* get a copy of the parents values for the current one ... */
502 wms_llbbox_t *local_llbbox = llbbox;
503 gboolean local_epsg4326 = epsg4326;
504
505 /* ... and overwrite the inherited stuff with better local stuff */
506 if(layer->llbbox.valid) local_llbbox = &layer->llbbox;
507 if(layer->epsg4326) local_epsg4326 = TRUE;
508
509 /* only named layers with useful bounding box are added to the list */
510 if(local_llbbox && layer->name) {
511 *c_layer = g_new0(wms_layer_t, 1);
512 (*c_layer)->name = g_strdup(layer->name);
513 (*c_layer)->title = g_strdup(layer->title);
514 (*c_layer)->epsg4326 = local_epsg4326;
515 (*c_layer)->llbbox = *local_llbbox;
516 c_layer = &((*c_layer)->next);
517 }
518
519 wms_get_child_layers(layer->children, depth+1,
520 local_epsg4326, local_llbbox,
521 c_layer);
522
523 layer = layer->next;
524 }
525 }
526
527 static wms_layer_t *wms_get_requestable_layers(wms_t *wms) {
528 printf("\nSearching for usable layers\n");
529
530 wms_layer_t *r_layer = NULL, **c_layer = &r_layer;
531
532 wms_layer_t *layer = wms->cap->layer;
533 while(layer) {
534 wms_llbbox_t *llbbox = &layer->llbbox;
535 if(llbbox && !llbbox->valid) llbbox = NULL;
536
537 wms_get_child_layers(layer->children, 1,
538 layer->epsg4326, llbbox, c_layer);
539
540 layer = layer->next;
541 }
542
543 return r_layer;
544 }
545
546 static gboolean wms_server_dialog(appdata_t *appdata, wms_t *wms) {
547 gboolean ok = FALSE;
548
549 GtkWidget *dialog = gtk_dialog_new_with_buttons(_("WMS Setup"),
550 GTK_WINDOW(appdata->window), GTK_DIALOG_MODAL,
551 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
552 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
553 NULL);
554
555 #ifdef USE_HILDON
556 /* making the dialog a little wider makes it less "crowded" */
557 gtk_window_set_default_size(GTK_WINDOW(dialog), 640, 50);
558 #else
559 gtk_window_set_default_size(GTK_WINDOW(dialog), 400, 50);
560 #endif
561
562 GtkWidget *label;
563 GtkWidget *table = gtk_table_new(2, 2, FALSE); // x, y
564
565 label = gtk_label_new(_("Server:"));
566 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
567 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, 0,0,0,0);
568 GtkWidget *server_entry = gtk_entry_new();
569 HILDON_ENTRY_NO_AUTOCAP(server_entry);
570 gtk_entry_set_text(GTK_ENTRY(server_entry), wms->server);
571 gtk_table_attach_defaults(GTK_TABLE(table), server_entry, 1, 2, 0, 1);
572
573 label = gtk_label_new(_("Path:"));
574 gtk_misc_set_alignment(GTK_MISC(label), 1.f, 0.5f);
575 gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, 0,0,0,0);
576 GtkWidget *path_entry = gtk_entry_new();
577 HILDON_ENTRY_NO_AUTOCAP(path_entry);
578 gtk_entry_set_text(GTK_ENTRY(path_entry), wms->path);
579 gtk_table_attach_defaults(GTK_TABLE(table), path_entry, 1, 2, 1, 2);
580
581 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), table);
582 gtk_widget_show_all(dialog);
583
584 if(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog))) {
585 wms->server = g_strdup(gtk_entry_get_text(GTK_ENTRY(server_entry)));
586 wms->path = g_strdup(gtk_entry_get_text(GTK_ENTRY(path_entry)));
587 ok = TRUE;
588 }
589
590 gtk_widget_destroy(dialog);
591
592 return ok;
593 }
594
595 enum {
596 LAYER_COL_TITLE = 0,
597 LAYER_COL_SELECTED,
598 LAYER_COL_FITS,
599 LAYER_COL_DATA,
600 LAYER_NUM_COLS
601 };
602
603 static void
604 layer_toggled(GtkCellRendererToggle *cell, const gchar *path_str,
605 GtkListStore *store) {
606 GtkTreePath *path;
607 GtkTreeIter iter;
608
609 path = gtk_tree_path_new_from_string(path_str);
610 gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path);
611
612 /* get current enabled flag */
613 gboolean enabled;
614 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
615 LAYER_COL_SELECTED, &enabled, -1);
616
617 /* change it and store it */
618 enabled = !enabled;
619 gtk_list_store_set(store, &iter, LAYER_COL_SELECTED, enabled, -1);
620
621 /* and store it in the layer itself */
622 wms_layer_t *layer = NULL;
623 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, LAYER_COL_DATA, &layer, -1);
624 layer->selected = enabled;
625
626 /* walk the entire store to get all values */
627 if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) {
628 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
629 LAYER_COL_SELECTED, &enabled, -1);
630
631 while(gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter) &&
632 !enabled)
633 gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
634 LAYER_COL_SELECTED, &enabled, -1);
635 }
636
637 GtkWidget *dialog = g_object_get_data(G_OBJECT(store), "dialog");
638 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog),
639 GTK_RESPONSE_ACCEPT, enabled);
640
641 gtk_tree_path_free(path);
642 }
643
644 static GtkWidget *wms_layer_widget(appdata_t *appdata, wms_layer_t *layer,
645 GtkWidget *dialog) {
646 GtkWidget *view = gtk_tree_view_new();
647
648 /* build the store */
649 GtkListStore *store = gtk_list_store_new(LAYER_NUM_COLS,
650 G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_POINTER);
651
652 g_object_set_data(G_OBJECT(store), "dialog", dialog);
653
654 /* --- "selected" column --- */
655 GtkCellRenderer *renderer = gtk_cell_renderer_toggle_new();
656 g_signal_connect(renderer, "toggled", G_CALLBACK(layer_toggled), store);
657 gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
658 -1, _(""), renderer,
659 "active", LAYER_COL_SELECTED,
660 "activatable", LAYER_COL_FITS,
661 NULL);
662
663 /* --- "Title" column --- */
664 renderer = gtk_cell_renderer_text_new();
665 g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL );
666 GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(
667 _("Title"), renderer,
668 "text", LAYER_COL_TITLE,
669 NULL);
670
671 gtk_tree_view_column_set_expand(column, TRUE);
672 gtk_tree_view_insert_column(GTK_TREE_VIEW(view), column, -1);
673
674 gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
675
676 GtkTreeIter iter;
677 while(layer) {
678 gboolean fits = wms_llbbox_fits(appdata->project, &layer->llbbox);
679
680 /* Append a row and fill in some data */
681 gtk_list_store_append(store, &iter);
682 gtk_list_store_set(store, &iter,
683 LAYER_COL_SELECTED, FALSE,
684 LAYER_COL_TITLE, layer->title,
685 LAYER_COL_FITS, fits,
686 LAYER_COL_DATA, layer,
687 -1);
688 layer = layer->next;
689 }
690
691 g_object_unref(store);
692
693 /* put it into a scrolled window */
694 GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
695 gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
696 GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
697 gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window),
698 GTK_SHADOW_ETCHED_IN);
699 gtk_container_add(GTK_CONTAINER(scrolled_window), view);
700
701 return scrolled_window;
702 }
703
704
705 static gboolean wms_layer_dialog(appdata_t *appdata, wms_layer_t *layer) {
706 gboolean ok = FALSE;
707
708 GtkWidget *dialog = gtk_dialog_new_with_buttons(_("WMS layer selection"),
709 GTK_WINDOW(appdata->window), GTK_DIALOG_MODAL,
710 GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
711 GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
712 NULL);
713
714 gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog),
715 GTK_RESPONSE_ACCEPT, FALSE);
716
717 #ifdef USE_HILDON
718 /* making the dialog a little wider makes it less "crowded" */
719 gtk_window_set_default_size(GTK_WINDOW(dialog), 640, 300);
720 #else
721 gtk_window_set_default_size(GTK_WINDOW(dialog), 400, 200);
722 #endif
723
724 /* layer list */
725 gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox),
726 wms_layer_widget(appdata, layer, dialog));
727
728
729 gtk_widget_show_all(dialog);
730
731 if(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
732 ok = TRUE;
733
734 gtk_widget_destroy(dialog);
735
736 return ok;
737 }
738
739 static gboolean wms_one_layer_is_usable(project_t *project,
740 wms_layer_t *layer) {
741 gboolean ok = FALSE;
742
743 while(layer) {
744 if(layer->name && layer->epsg4326 && layer->llbbox.valid)
745 ok = TRUE;
746
747 printf("----- Layer \"%s\" -----\n", layer->title);
748 printf("Name: %s\n", layer->name);
749 printf("epsg4326: %s\n", layer->epsg4326?"yes":"no");
750 if(layer->llbbox.valid) {
751 printf("llbbox: %f/%f %f/%f\n",
752 layer->llbbox.min.lat, layer->llbbox.min.lon,
753 layer->llbbox.max.lat, layer->llbbox.max.lon);
754
755 printf("llbbox fits project: %s\n",
756 wms_llbbox_fits(project, &layer->llbbox)?"yes":"no");
757 } else
758 printf("llbbox: none/invalid\n");
759
760 layer = layer->next;
761 }
762
763 return ok;
764 }
765
766 void wms_import(appdata_t *appdata) {
767 if(!appdata->project) {
768 errorf(GTK_WIDGET(appdata->window),
769 _("Need an open project to derive WMS coordinates"));
770 return;
771 }
772
773 /* this cancels any wms adjustment in progress */
774 if(appdata->map->action.type == MAP_ACTION_BG_ADJUST)
775 map_action_cancel(appdata);
776
777 wms_t *wms = g_new0(wms_t,1);
778 wms->server = g_strdup(appdata->project->wms_server);
779 wms->path = g_strdup(appdata->project->wms_path);
780
781 /* reset any background adjustments in the project ... */
782 if((appdata->project->wms_offset.x != 0)||
783 (appdata->project->wms_offset.y != 0)) {
784
785 appdata->project->wms_offset.x = 0;
786 appdata->project->wms_offset.y = 0;
787 appdata->project->dirty = TRUE;
788 }
789
790 /* ... as well as in the map */
791 appdata->map->bg.offset.x = 0;
792 appdata->map->bg.offset.y = 0;
793
794 /* get server from dialog */
795 if(!wms_server_dialog(appdata, wms)) {
796 wms_free(wms);
797 return;
798 }
799
800 /* ------------- copy values back into project ---------------- */
801 if(strcmp(appdata->project->wms_server, wms->server) != 0) {
802 g_free(appdata->project->wms_server);
803 appdata->project->wms_server = g_strdup(wms->server);
804 appdata->project->dirty = TRUE;
805 }
806
807 if(strcmp(appdata->project->wms_path, wms->path) != 0) {
808 g_free(appdata->project->wms_path);
809 appdata->project->wms_path = g_strdup(wms->path);
810 appdata->project->dirty = TRUE;
811 }
812
813 /* ----------- request capabilities -------------- */
814 gboolean path_contains_qm = (strchr(wms->path, '?') != NULL);
815 gboolean path_ends_with_special =
816 (wms->path[strlen(wms->path)-1] == '?') ||
817 (wms->path[strlen(wms->path)-1] == '&');
818
819 /* if there's already a question mark, then add further */
820 /* parameters using the &, else use the ? */
821 char *append_char = path_ends_with_special?"":(path_contains_qm?"&":"?");
822
823 char *url = g_strdup_printf("%s%s"
824 "%sSERVICE=wms"
825 "&WMTVER=1.1.1"
826 "&REQUEST=GetCapabilities",
827 wms->server, wms->path, append_char);
828
829 char *cap = wms_download(url, NULL);
830 g_free(url);
831
832 /* ----------- parse capabilities -------------- */
833
834 if(!cap) {
835 errorf(GTK_WIDGET(appdata->window),
836 _("WMS download failed:\n\n"
837 "GetCapabilities failed"));
838 } else {
839 xmlDoc *doc = NULL;
840
841 LIBXML_TEST_VERSION;
842
843 /* parse the file and get the DOM */
844 if((doc = xmlReadMemory(cap, strlen(cap), NULL, NULL, 0)) == NULL) {
845 xmlErrorPtr errP = xmlGetLastError();
846 errorf(GTK_WIDGET(appdata->window),
847 _("WMS download failed:\n\n"
848 "XML error while parsing capabilities:\n"
849 "%s"), errP->message);
850 } else {
851 printf("ok, parse doc tree\n");
852
853 wms_cap_parse_doc(wms, doc);
854 }
855
856 g_free(cap);
857 }
858
859 /* ------------ basic checks ------------- */
860
861 if(!wms->cap || !wms->service || !wms->cap->layer ||
862 !wms->cap->request || !wms->cap->request->getmap) {
863 errorf(GTK_WIDGET(appdata->window), _("Incomplete/unexpected reply!"));
864 wms_free(wms);
865 return;
866 }
867
868 if(!wms->cap->request->getmap->format) {
869 errorf(GTK_WIDGET(appdata->window), _("No supported image format found."));
870 wms_free(wms);
871 return;
872 }
873
874 /* ---------- evaluate layers ----------- */
875
876 wms_layer_t *layer = wms_get_requestable_layers(wms);
877 gboolean at_least_one_ok = wms_one_layer_is_usable(appdata->project, layer);
878
879 if(!at_least_one_ok) {
880 errorf(GTK_WIDGET(appdata->window),
881 _("Server provides no data in the required format!\n"
882 "(epsg4326 and LatLonBoundingBox are mandatory for osm2go)"));
883 wms_layer_free(layer);
884 wms_free(wms);
885 return;
886 }
887
888 if(!wms_layer_dialog(appdata, layer)) {
889 wms_layer_free(layer);
890 wms_free(wms);
891 return;
892 }
893
894 /* --------- build getmap request ----------- */
895
896 /* get required image size */
897 wms_setup_extent(appdata->project, wms);
898
899 /* start building url */
900 url = g_strdup_printf("%s%s"
901 "%sSERVICE=wms"
902 "&WMTVER=1.1.1"
903 "&REQUEST=GetMap"
904 "&LAYERS=",
905 wms->server, wms->path, append_char);
906
907 /* append layers */
908 char *old;
909 wms_layer_t *t = layer;
910 gint cnt = 0;
911 while(t) {
912 if(t->selected) {
913 old = url;
914 url = g_strconcat(url, (!cnt)?"":",", t->name, NULL);
915 g_free(old);
916 cnt++;
917 }
918 t = t->next;
919 }
920 wms_layer_free(layer);
921
922 /* append styles entry */
923 old = url;
924 url = g_strconcat(url, "&STYLES=", NULL);
925 g_free(old);
926
927 while(--cnt) {
928 old = url;
929 url = g_strconcat(url, ",", NULL);
930 g_free(old);
931 }
932
933 /* and append rest */
934 char minlon[G_ASCII_DTOSTR_BUF_SIZE], minlat[G_ASCII_DTOSTR_BUF_SIZE];
935 char maxlon[G_ASCII_DTOSTR_BUF_SIZE], maxlat[G_ASCII_DTOSTR_BUF_SIZE];
936
937 g_ascii_dtostr(minlon, sizeof(minlon), appdata->project->min.lon);
938 g_ascii_dtostr(minlat, sizeof(minlat), appdata->project->min.lat);
939 g_ascii_dtostr(maxlon, sizeof(maxlon), appdata->project->max.lon);
940 g_ascii_dtostr(maxlat, sizeof(maxlat), appdata->project->max.lat);
941
942 gint format = 0;
943 while(!(wms->cap->request->getmap->format & (1<<format)))
944 format++;
945
946 const char *formats[] = { "image/jpg", "image/jpeg",
947 "image/png", "image/gif" };
948
949 old = url;
950 url = g_strdup_printf("%s&SRS=EPSG:4326&BBOX=%s,%s,%s,%s"
951 "&WIDTH=%d&HEIGHT=%d&FORMAT=%s&reaspect=false", url,
952 minlon, minlat, maxlon, maxlat, wms->width,
953 wms->height, formats[format]);
954 g_free(old);
955
956 const char *exts[] = { "jpg", "jpg", "png", "gif" };
957 char *filename = g_strdup_printf("%s/wms.%s", appdata->project->path,
958 exts[format]);
959
960
961 /* remove any existing image before */
962 wms_remove(appdata);
963
964 if(!wms_download(url, filename)) {
965 g_free(filename);
966 g_free(url);
967 wms_free(wms);
968 return;
969 }
970
971 /* there should be a matching file on disk now */
972 map_set_bg_image(appdata->map, filename);
973
974 g_free(filename);
975 g_free(url);
976
977 /* --------- free wms structure -----------------*/
978 wms_free(wms);
979
980 gtk_widget_set_sensitive(appdata->menu_item_wms_clear, TRUE);
981 gtk_widget_set_sensitive(appdata->menu_item_wms_adjust, TRUE);
982 }
983
984 /* try to load an existing image into map */
985 void wms_load(appdata_t *appdata) {
986 const char *exts[] = { "png", "gif", "jpg", "" };
987 int i=0;
988
989 while(exts[i][0]) {
990 char *filename = g_strdup_printf("%s/wms.%s", appdata->project->path,
991 exts[i]);
992
993 if(g_file_test(filename, G_FILE_TEST_EXISTS)) {
994 appdata->map->bg.offset.x = appdata->project->wms_offset.x;
995 appdata->map->bg.offset.y = appdata->project->wms_offset.y;
996
997 map_set_bg_image(appdata->map, filename);
998
999 /* restore image to saved position */
1000 gint x = appdata->osm->bounds->min.x + appdata->map->bg.offset.x;
1001 gint y = appdata->osm->bounds->min.y + appdata->map->bg.offset.y;
1002 canvas_image_move(appdata->map->bg.item, x, y,
1003 appdata->map->bg.scale.x, appdata->map->bg.scale.y);
1004
1005 g_free(filename);
1006
1007 gtk_widget_set_sensitive(appdata->menu_item_wms_clear, TRUE);
1008 gtk_widget_set_sensitive(appdata->menu_item_wms_adjust, TRUE);
1009
1010 return;
1011 }
1012 g_free(filename);
1013 i++;
1014 }
1015 }
1016
1017 void wms_remove(appdata_t *appdata) {
1018 const char *exts[] = { "png", "gif", "jpg", "" };
1019 int i=0;
1020
1021 /* this cancels any wms adjustment in progress */
1022 if(appdata->map->action.type == MAP_ACTION_BG_ADJUST)
1023 map_action_cancel(appdata);
1024
1025 gtk_widget_set_sensitive(appdata->menu_item_wms_clear, FALSE);
1026 gtk_widget_set_sensitive(appdata->menu_item_wms_adjust, FALSE);
1027
1028 map_remove_bg_image(appdata->map);
1029
1030 while(exts[i][0]) {
1031 char *filename = g_strdup_printf("%s/wms.%s", appdata->project->path,
1032 exts[i]);
1033
1034 if(g_file_test(filename, G_FILE_TEST_EXISTS))
1035 g_remove(filename);
1036
1037 g_free(filename);
1038 i++;
1039 }
1040 }
1041