Contents of /trunk/src/wms.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 14 - (hide annotations)
Mon Dec 15 19:45:38 2008 UTC (15 years, 5 months ago) by harbaum
File MIME type: text/plain
File size: 43101 byte(s)
WMS server selection redone, other small changes and bugfixes
1 harbaum 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 <libxml/parser.h>
23     #include <libxml/tree.h>
24    
25     #ifndef LIBXML_TREE_ENABLED
26     #error "Tree not enabled in libxml"
27     #endif
28    
29     #define WMS_FORMAT_JPG (1<<0)
30     #define WMS_FORMAT_JPEG (1<<1)
31     #define WMS_FORMAT_PNG (1<<2)
32     #define WMS_FORMAT_GIF (1<<3)
33    
34     typedef struct {
35     pos_t min, max;
36     gboolean valid;
37     } wms_llbbox_t;
38    
39     typedef struct wms_layer_s {
40     char *title;
41     char *name;
42     gboolean epsg4326, selected;
43     wms_llbbox_t llbbox;
44    
45     struct wms_layer_s *children;
46     struct wms_layer_s *next;
47     } wms_layer_t;
48    
49     typedef struct {
50     gulong format;
51     } wms_getmap_t;
52    
53     typedef struct {
54     wms_getmap_t *getmap;
55     } wms_request_t;
56    
57     typedef struct {
58     char *title;
59     } wms_service_t;
60    
61     typedef struct {
62     wms_layer_t *layer;
63     wms_request_t *request;
64     } wms_cap_t;
65    
66     typedef struct {
67     char *server;
68     char *path;
69     gint width, height;
70    
71     wms_service_t *service;
72     wms_cap_t *cap;
73     } wms_t;
74    
75     gboolean xmlTextIs(xmlDocPtr doc, xmlNodePtr list, char *str) {
76     char *nstr = (char*)xmlNodeListGetString(doc, list, 1);
77     if(!nstr) return FALSE;
78    
79     printf("text = %s\n", nstr);
80    
81     gboolean match = (strcmp(str, nstr) == 0);
82     xmlFree(nstr);
83     return match;
84     }
85    
86     gboolean xmlPropIs(xmlNode *node, char *prop, char *str) {
87     char *prop_str = (char*)xmlGetProp(node, BAD_CAST prop);
88     if(!prop_str) return FALSE;
89    
90     gboolean match = (strcmp(prop_str, str) == 0);
91     xmlFree(prop_str);
92     return match;
93     }
94    
95     float xmlGetPropFloat(xmlNode *node, char *prop) {
96     char *prop_str = (char*)xmlGetProp(node, BAD_CAST prop);
97     if(!prop_str) return NAN;
98    
99     float retval = g_ascii_strtod(prop_str, NULL);
100     xmlFree(prop_str);
101     return retval;
102     }
103    
104     static gboolean wms_bbox_is_valid(pos_t *min, pos_t *max) {
105     /* all four coordinates are valid? */
106     if(isnan(min->lat)||isnan(min->lon)||isnan(max->lat)||isnan(max->lon))
107     return FALSE;
108    
109     /* min/max span a useful range? */
110     if(max->lat - min->lat < 1.0) return FALSE;
111     if(max->lon - min->lon < 1.0) return FALSE;
112    
113     /* useful angles? */
114     if(min->lat > 90.0 || min->lat < -90.0) return FALSE;
115     if(max->lat > 90.0 || max->lat < -90.0) return FALSE;
116     if(min->lon > 180.0 || min->lon < -180.0) return FALSE;
117     if(max->lon > 180.0 || max->lon < -180.0) return FALSE;
118    
119     return TRUE;
120     }
121    
122     static wms_layer_t *wms_cap_parse_layer(xmlDocPtr doc, xmlNode *a_node) {
123     wms_layer_t *wms_layer = NULL;
124     xmlNode *cur_node = NULL;
125     char *str = NULL;
126    
127     wms_layer = g_new0(wms_layer_t, 1);
128     wms_layer->llbbox.min.lon = wms_layer->llbbox.min.lat = NAN;
129     wms_layer->llbbox.max.lon = wms_layer->llbbox.max.lat = NAN;
130    
131     wms_layer_t **children = &(wms_layer->children);
132    
133     for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
134     if (cur_node->type == XML_ELEMENT_NODE) {
135     if(strcasecmp((char*)cur_node->name, "Layer") == 0) {
136     *children = wms_cap_parse_layer(doc, cur_node);
137     if(*children) children = &((*children)->next);
138     } else if(strcasecmp((char*)cur_node->name, "Name") == 0) {
139     str = (char*)xmlNodeListGetString(doc, cur_node->children, 1);
140     wms_layer->name = g_strdup(str);
141     xmlFree(str);
142     } else if(strcasecmp((char*)cur_node->name, "Title") == 0) {
143     str = (char*)xmlNodeListGetString(doc, cur_node->children, 1);
144     wms_layer->title = g_strdup(str);
145     xmlFree(str);
146     } else if(strcasecmp((char*)cur_node->name, "SRS") == 0) {
147     if(xmlTextIs(doc, cur_node->children, "EPSG:4326"))
148     wms_layer->epsg4326 = TRUE;
149     } else if(strcasecmp((char*)cur_node->name, "LatLonBoundingBox") == 0) {
150     wms_layer->llbbox.min.lat = xmlGetPropFloat(cur_node, "miny");
151     wms_layer->llbbox.min.lon = xmlGetPropFloat(cur_node, "minx");
152     wms_layer->llbbox.max.lat = xmlGetPropFloat(cur_node, "maxy");
153     wms_layer->llbbox.max.lon = xmlGetPropFloat(cur_node, "maxx");
154     } else
155     printf("found unhandled WMT_MS_Capabilities/Capability/Layer/%s\n",
156     cur_node->name);
157     }
158     }
159    
160     wms_layer->llbbox.valid = wms_bbox_is_valid(&wms_layer->llbbox.min,
161     &wms_layer->llbbox.max);
162    
163     printf("------------------- Layer: %s ---------------------------\n",
164     wms_layer->title);
165     printf("Name: %s\n", wms_layer->name);
166     printf("EPSG-4326: %s\n", wms_layer->epsg4326?"yes":"no");
167     if(wms_layer->llbbox.valid)
168     printf("LatLonBBox: %f/%f %f/%f\n",
169     wms_layer->llbbox.min.lat, wms_layer->llbbox.min.lon,
170     wms_layer->llbbox.max.lat, wms_layer->llbbox.max.lon);
171     else
172     printf("No/invalid LatLonBBox\n");
173    
174     return wms_layer;
175     }
176    
177     static wms_getmap_t *wms_cap_parse_getmap(xmlDocPtr doc, xmlNode *a_node) {
178     wms_getmap_t *wms_getmap = NULL;
179     xmlNode *cur_node = NULL;
180    
181     wms_getmap = g_new0(wms_getmap_t, 1);
182    
183     for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
184     if (cur_node->type == XML_ELEMENT_NODE) {
185     if(strcasecmp((char*)cur_node->name, "Format") == 0) {
186     if(xmlTextIs(doc, cur_node->children, "image/png"))
187     wms_getmap->format |= WMS_FORMAT_PNG;
188     if(xmlTextIs(doc, cur_node->children, "image/gif"))
189     wms_getmap->format |= WMS_FORMAT_GIF;
190     if(xmlTextIs(doc, cur_node->children, "image/jpg"))
191     wms_getmap->format |= WMS_FORMAT_JPG;
192     if(xmlTextIs(doc, cur_node->children, "image/jpeg"))
193     wms_getmap->format |= WMS_FORMAT_JPEG;
194     } else
195     printf("found unhandled "
196     "WMT_MS_Capabilities/Capability/Request/GetMap/%s\n",
197     cur_node->name);
198     }
199     }
200    
201     printf("Supported formats: %s%s%s\n",
202     (wms_getmap->format & WMS_FORMAT_PNG)?"png ":"",
203     (wms_getmap->format & WMS_FORMAT_GIF)?"gif ":"",
204     (wms_getmap->format & (WMS_FORMAT_JPG | WMS_FORMAT_JPEG))?"jpg ":"");
205     return wms_getmap;
206     }
207    
208     static wms_request_t *wms_cap_parse_request(xmlDocPtr doc, xmlNode *a_node) {
209     wms_request_t *wms_request = NULL;
210     xmlNode *cur_node = NULL;
211    
212     wms_request = g_new0(wms_request_t, 1);
213    
214     for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
215     if (cur_node->type == XML_ELEMENT_NODE) {
216     if(strcasecmp((char*)cur_node->name, "GetMap") == 0) {
217     wms_request->getmap = wms_cap_parse_getmap(doc, cur_node);
218     } else
219     printf("found unhandled WMT_MS_Capabilities/Capability/Request/%s\n",
220     cur_node->name);
221     }
222     }
223    
224     return wms_request;
225     }
226    
227     static wms_cap_t *wms_cap_parse_cap(xmlDocPtr doc, xmlNode *a_node) {
228     wms_cap_t *wms_cap = NULL;
229     xmlNode *cur_node = NULL;
230    
231     wms_cap = g_new0(wms_cap_t, 1);
232     wms_layer_t **layer = &(wms_cap->layer);
233    
234     for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
235     if (cur_node->type == XML_ELEMENT_NODE) {
236     if(strcasecmp((char*)cur_node->name, "Request") == 0) {
237     wms_cap->request = wms_cap_parse_request(doc, cur_node);
238     } else if(strcasecmp((char*)cur_node->name, "Layer") == 0) {
239     *layer = wms_cap_parse_layer(doc, cur_node);
240     if(*layer) layer = &((*layer)->next);
241     } else
242     printf("found unhandled WMT_MS_Capabilities/Capability/%s\n",
243     cur_node->name);
244     }
245     }
246    
247     return wms_cap;
248     }
249    
250     static wms_service_t *wms_cap_parse_service(xmlDocPtr doc, xmlNode *a_node) {
251     wms_service_t *wms_service = NULL;
252     xmlNode *cur_node = NULL;
253     char *str = NULL;
254    
255     wms_service = g_new0(wms_service_t, 1);
256    
257     for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
258     if (cur_node->type == XML_ELEMENT_NODE) {
259     if(strcasecmp((char*)cur_node->name, "title") == 0) {
260     str = (char*)xmlNodeListGetString(doc, cur_node->children, 1);
261     wms_service->title = g_strdup(str);
262     xmlFree(str);
263     } else
264     printf("found unhandled WMT_MS_Capabilities/Service/%s\n",
265     cur_node->name);
266     }
267     }
268    
269     printf("-- Service --\n");
270     printf("Title: %s\n", wms_service->title);
271    
272     return wms_service;
273     }
274    
275     static void wms_cap_parse(wms_t *wms, xmlDocPtr doc, xmlNode *a_node) {
276     xmlNode *cur_node = NULL;
277    
278     for (cur_node = a_node->children; cur_node; cur_node = cur_node->next) {
279     if (cur_node->type == XML_ELEMENT_NODE) {
280    
281     if(strcasecmp((char*)cur_node->name, "Service") == 0) {
282     if(!wms->service)
283     wms->service = wms_cap_parse_service(doc, cur_node);
284     } else if(strcasecmp((char*)cur_node->name, "Capability") == 0) {
285     if(!wms->cap)
286     wms->cap = wms_cap_parse_cap(doc, cur_node);
287     } else
288     printf("found unhandled WMT_MS_Capabilities/%s\n", cur_node->name);
289     }
290     }
291     }
292    
293     /* parse root element */
294     static void wms_cap_parse_root(wms_t *wms, xmlDocPtr doc, xmlNode *a_node) {
295     xmlNode *cur_node = NULL;
296    
297     for (cur_node = a_node; cur_node; cur_node = cur_node->next) {
298     if (cur_node->type == XML_ELEMENT_NODE) {
299    
300     if(strcasecmp((char*)cur_node->name, "WMT_MS_Capabilities") == 0) {
301     wms_cap_parse(wms, doc, cur_node);
302     } else
303     printf("found unhandled %s\n", cur_node->name);
304     }
305     }
306     }
307    
308     static void wms_cap_parse_doc(wms_t *wms, xmlDocPtr doc) {
309     /* Get the root element node */
310     xmlNode *root_element = xmlDocGetRootElement(doc);
311    
312     wms_cap_parse_root(wms, doc, root_element);
313    
314     /*free the document */
315     xmlFreeDoc(doc);
316    
317     /*
318     * Free the global variables that may
319     * have been allocated by the parser.
320     */
321     xmlCleanupParser();
322     }
323    
324     /* get pixel extent of image display */
325     void wms_setup_extent(project_t *project, wms_t *wms) {
326     pos_t center;
327     lpos_t lcenter, lmin, lmax;
328     float scale;
329    
330     center.lat = (project->min.lat + project->max.lat)/2;
331     center.lon = (project->min.lon + project->max.lon)/2;
332    
333     pos2lpos_center(&center, &lcenter);
334    
335     /* the scale is needed to accomodate for "streching" */
336     /* by the mercartor projection */
337     scale = cos(DEG2RAD(center.lat));
338    
339     pos2lpos_center(&project->min, &lmin);
340     lmin.x -= lcenter.x;
341     lmin.y -= lcenter.y;
342     lmin.x *= scale;
343     lmin.y *= scale;
344    
345     pos2lpos_center(&project->max, &lmax);
346     lmax.x -= lcenter.x;
347     lmax.y -= lcenter.y;
348     lmax.x *= scale;
349     lmax.y *= scale;
350    
351     wms->width = lmax.x - lmin.x;
352     wms->height = lmax.y - lmin.y;
353    
354     if(wms->width > 2048) wms->width = 2048;
355     if(wms->height > 2048) wms->height = 2048;
356    
357     printf("WMS: required image size = %dx%d\n",
358     wms->width, wms->height);
359     }
360    
361     /* --------------- freeing stuff ------------------- */
362    
363     static void wms_layer_free(wms_layer_t *layer) {
364     while(layer) {
365    
366     if(layer->title) g_free(layer->title);
367     if(layer->name) g_free(layer->name);
368    
369     if(layer->children) wms_layer_free(layer->children);
370    
371     wms_layer_t *next = layer->next;
372     g_free(layer);
373     layer = next;
374     }
375     }
376    
377     static void wms_getmap_free(wms_getmap_t *getmap) {
378     g_free(getmap);
379     }
380    
381     static void wms_request_free(wms_request_t *request) {
382     if(request->getmap) wms_getmap_free(request->getmap);
383     g_free(request);
384     }
385    
386     static void wms_cap_free(wms_cap_t *cap) {
387     if(cap->layer) wms_layer_free(cap->layer);
388     if(cap->request) wms_request_free(cap->request);
389     g_free(cap);
390     }
391    
392     static void wms_service_free(wms_service_t *service) {
393     if(service->title) g_free(service->title);
394     g_free(service);
395     }
396    
397     static void wms_free(wms_t *wms) {
398     if(wms->server) g_free(wms->server);
399     if(wms->path) g_free(wms->path);
400     if(wms->cap) wms_cap_free(wms->cap);
401     if(wms->service) wms_service_free(wms->service);
402     g_free(wms);
403     }
404    
405     /* ---------------------- use ------------------- */
406    
407     static gboolean wms_llbbox_fits(project_t *project, wms_llbbox_t *llbbox) {
408     if(!llbbox || !llbbox->valid ||
409     (project->min.lat < llbbox->min.lat) ||
410     (project->min.lon < llbbox->min.lon) ||
411     (project->max.lat > llbbox->max.lat) ||
412     (project->max.lon > llbbox->max.lon))
413     return FALSE;
414    
415     return TRUE;
416     }
417    
418     static void wms_get_child_layers(wms_layer_t *layer,
419     gint depth, gboolean epsg4326, wms_llbbox_t *llbbox,
420     wms_layer_t **c_layer) {
421     while(layer) {
422    
423     /* get a copy of the parents values for the current one ... */
424     wms_llbbox_t *local_llbbox = llbbox;
425     gboolean local_epsg4326 = epsg4326;
426    
427     /* ... and overwrite the inherited stuff with better local stuff */
428     if(layer->llbbox.valid) local_llbbox = &layer->llbbox;
429     if(layer->epsg4326) local_epsg4326 = TRUE;
430    
431     /* only named layers with useful bounding box are added to the list */
432     if(local_llbbox && layer->name) {
433     *c_layer = g_new0(wms_layer_t, 1);
434     (*c_layer)->name = g_strdup(layer->name);
435     (*c_layer)->title = g_strdup(layer->title);
436     (*c_layer)->epsg4326 = local_epsg4326;
437     (*c_layer)->llbbox = *local_llbbox;
438     c_layer = &((*c_layer)->next);
439     }
440    
441     wms_get_child_layers(layer->children, depth+1,
442     local_epsg4326, local_llbbox,
443     c_layer);
444    
445     layer = layer->next;
446     }
447     }
448    
449     static wms_layer_t *wms_get_requestable_layers(wms_t *wms) {
450     printf("\nSearching for usable layers\n");
451    
452     wms_layer_t *r_layer = NULL, **c_layer = &r_layer;
453    
454     wms_layer_t *layer = wms->cap->layer;
455     while(layer) {
456     wms_llbbox_t *llbbox = &layer->llbbox;
457     if(llbbox && !llbbox->valid) llbbox = NULL;
458    
459     wms_get_child_layers(layer->children, 1,
460     layer->epsg4326, llbbox, c_layer);
461    
462     layer = layer->next;
463     }
464    
465     return r_layer;
466     }
467    
468 harbaum 14 enum {
469     WMS_SERVER_COL_NAME = 0,
470     WMS_SERVER_COL_DATA,
471     WMS_SERVER_NUM_COLS
472     };
473    
474     typedef struct {
475     appdata_t *appdata;
476     wms_t *wms;
477     GtkWidget *dialog, *view;
478     GtkListStore *store;
479     GtkWidget *but_add, *but_edit, *but_remove;
480     GtkWidget *server_label, *path_label;
481     } wms_server_context_t;
482    
483     static wms_server_t *get_selection(wms_server_context_t *context) {
484     GtkTreeSelection *selection;
485     GtkTreeModel *model;
486     GtkTreeIter iter;
487    
488     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(context->view));
489     if(gtk_tree_selection_get_selected(selection, &model, &iter)) {
490     wms_server_t *wms_server;
491     gtk_tree_model_get(model, &iter, WMS_SERVER_COL_DATA, &wms_server, -1);
492     return(wms_server);
493     }
494    
495     return NULL;
496     }
497    
498     static void wms_server_selected(wms_server_context_t *context,
499     wms_server_t *selected) {
500    
501     if(!selected && context->wms->server && context->wms->path) {
502     /* if the projects settings match a list entry, then select this */
503    
504     GtkTreeSelection *selection =
505     gtk_tree_view_get_selection(GTK_TREE_VIEW(context->view));
506    
507     /* walk the entire store to get all values */
508     wms_server_t *server = NULL;
509     GtkTreeIter iter;
510    
511     gboolean valid =
512     gtk_tree_model_get_iter_first(GTK_TREE_MODEL(context->store), &iter);
513    
514     while(valid && !selected) {
515     gtk_tree_model_get(GTK_TREE_MODEL(context->store), &iter,
516     WMS_SERVER_COL_DATA, &server, -1);
517     g_assert(server);
518    
519     if((strcmp(server->server, context->wms->server) == 0) &&
520     (strcmp(server->path, context->wms->path) == 0)) {
521     gtk_tree_selection_select_iter(selection, &iter);
522     selected = server;
523     }
524    
525     valid = gtk_tree_model_iter_next(GTK_TREE_MODEL(context->store), &iter);
526     }
527     }
528    
529     gtk_widget_set_sensitive(context->but_remove, selected != NULL);
530     gtk_widget_set_sensitive(context->but_edit, selected != NULL);
531    
532     /* user can click ok if a entry is selected or if both fields are */
533     /* otherwise valid */
534     if(selected) {
535     gtk_dialog_set_response_sensitive(GTK_DIALOG(context->dialog),
536     GTK_RESPONSE_ACCEPT, TRUE);
537    
538     gtk_label_set_text(GTK_LABEL(context->server_label), selected->server);
539     gtk_label_set_text(GTK_LABEL(context->path_label), selected->path);
540     } else {
541     gtk_dialog_set_response_sensitive(GTK_DIALOG(context->dialog),
542     GTK_RESPONSE_ACCEPT, context->wms->server && context->wms->path);
543    
544     if(context->wms->server)
545     gtk_label_set_text(GTK_LABEL(context->server_label),
546     context->wms->server);
547     if(context->wms->path)
548     gtk_label_set_text(GTK_LABEL(context->path_label),
549     context->wms->path);
550     }
551     }
552    
553     static gboolean
554     wms_server_selection_func(GtkTreeSelection *selection, GtkTreeModel *model,
555     GtkTreePath *path, gboolean path_currently_selected,
556     gpointer userdata) {
557     wms_server_context_t *context = (wms_server_context_t*)userdata;
558     GtkTreeIter iter;
559    
560     if(gtk_tree_model_get_iter(model, &iter, path)) {
561     wms_server_t *wms_server = NULL;
562    
563     g_assert(gtk_tree_path_get_depth(path) == 1);
564     gtk_tree_model_get(model, &iter, WMS_SERVER_COL_DATA, &wms_server, -1);
565     wms_server_selected(context, wms_server);
566     }
567    
568     return TRUE; /* allow selection state to change */
569     }
570    
571     static void on_server_remove(GtkWidget *but, wms_server_context_t *context) {
572     GtkTreeSelection *selection;
573     GtkTreeModel *model;
574     GtkTreeIter iter;
575    
576     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(context->view));
577     if(gtk_tree_selection_get_selected(selection, &model, &iter)) {
578     wms_server_t *server = NULL;
579     gtk_tree_model_get(model, &iter, WMS_SERVER_COL_DATA, &server, -1);
580    
581     g_assert(server);
582    
583     /* de-chain */
584     printf("de-chaining server %s\n", server->name);
585     wms_server_t **prev = &context->appdata->settings->wms_server;
586     while(*prev != server) prev = &((*prev)->next);
587     *prev = server->next;
588    
589     /* free tag itself */
590     wms_server_free(server);
591    
592     /* and remove from store */
593     gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
594     }
595    
596     wms_server_selected(context, NULL);
597     }
598    
599     static void callback_modified_name(GtkWidget *widget, gpointer data) {
600     wms_server_context_t *context = (wms_server_context_t*)data;
601    
602     char *name = (char*)gtk_entry_get_text(GTK_ENTRY(widget));
603    
604     /* name must not contain some special chars */
605     gboolean ok = TRUE;
606    
607     /* search all entries except the last (which is the one we are editing) */
608     wms_server_t *server = context->appdata->settings->wms_server;
609     while(server && server->next) {
610     if(strcasecmp(server->name, name) == 0) ok = FALSE;
611     server = server->next;
612     }
613    
614     GtkWidget *toplevel = gtk_widget_get_toplevel(widget);
615     /* toplevel is a dialog only of dialog has been realized */
616     if(GTK_IS_DIALOG(toplevel))
617     gtk_dialog_set_response_sensitive(
618     GTK_DIALOG(gtk_widget_get_toplevel(widget)),
619     GTK_RESPONSE_ACCEPT, ok);
620     }
621    
622     /* edit url and path of a given wms server entry */
623     gboolean wms_server_edit(wms_server_context_t *context, gboolean edit_name,
624     wms_server_t *wms_server) {
625     GtkWidget *dialog = gtk_dialog_new_with_buttons(_("Edit WMS Server"),
626     GTK_WINDOW(context->dialog), GTK_DIALOG_MODAL,
627     GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
628     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
629     NULL);
630    
631     #ifdef USE_HILDON
632     gtk_window_set_default_size(GTK_WINDOW(dialog), 500, 50);
633     #else
634     gtk_window_set_default_size(GTK_WINDOW(dialog), 400, 50);
635     #endif
636    
637     gtk_dialog_set_default_response(GTK_DIALOG(dialog),
638     GTK_RESPONSE_ACCEPT);
639    
640     GtkWidget *label, *name, *server, *path;
641     GtkWidget *table = gtk_table_new(2, 3, FALSE);
642    
643     gtk_table_attach(GTK_TABLE(table),
644     label = gtk_label_new(_("Name:")), 0, 1, 0, 1,
645     GTK_FILL, 0, 0, 0);
646     gtk_misc_set_alignment(GTK_MISC(label), 0.f, 0.5f);
647     gtk_table_attach_defaults(GTK_TABLE(table),
648     name = gtk_entry_new(), 1, 2, 0, 1);
649     gtk_entry_set_activates_default(GTK_ENTRY(name), TRUE);
650     HILDON_ENTRY_NO_AUTOCAP(name);
651     gtk_widget_set_sensitive(GTK_WIDGET(name), edit_name);
652     g_signal_connect(G_OBJECT(name), "changed",
653     G_CALLBACK(callback_modified_name), context);
654    
655     gtk_table_attach(GTK_TABLE(table),
656     label = gtk_label_new(_("Server:")), 0, 1, 1, 2,
657     GTK_FILL, 0, 0, 0);
658     gtk_misc_set_alignment(GTK_MISC(label), 0.f, 0.5f);
659     gtk_table_attach_defaults(GTK_TABLE(table),
660     server = gtk_entry_new(), 1, 2, 1, 2);
661     gtk_entry_set_activates_default(GTK_ENTRY(server), TRUE);
662     HILDON_ENTRY_NO_AUTOCAP(server);
663    
664     gtk_table_attach(GTK_TABLE(table),
665     label = gtk_label_new(_("Path:")), 0, 1, 2, 3,
666     GTK_FILL, 0, 0, 0);
667     gtk_misc_set_alignment(GTK_MISC(label), 0.f, 0.5f);
668     gtk_table_attach_defaults(GTK_TABLE(table),
669     path = gtk_entry_new(), 1, 2, 2, 3);
670     gtk_entry_set_activates_default(GTK_ENTRY(path), TRUE);
671     HILDON_ENTRY_NO_AUTOCAP(path);
672    
673     gtk_entry_set_text(GTK_ENTRY(name), wms_server->name);
674     gtk_entry_set_text(GTK_ENTRY(server), wms_server->server);
675     gtk_entry_set_text(GTK_ENTRY(path), wms_server->path);
676    
677     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox), table);
678    
679     gtk_widget_show_all(dialog);
680    
681     if(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog))) {
682     if(edit_name) {
683     GtkTreeSelection *selection;
684     GtkTreeModel *model;
685     GtkTreeIter iter;
686     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(context->view));
687     gtk_tree_selection_get_selected(selection, &model, &iter);
688     gtk_list_store_set(context->store, &iter,
689     WMS_SERVER_COL_NAME, wms_server->name,
690     -1);
691    
692     g_free(wms_server->name);
693     wms_server->name = strdup((char*)gtk_entry_get_text(GTK_ENTRY(name)));
694     }
695    
696     g_free(wms_server->server);
697     wms_server->server = strdup((char*)gtk_entry_get_text(GTK_ENTRY(server)));
698     g_free(wms_server->path);
699     wms_server->path = strdup((char*)gtk_entry_get_text(GTK_ENTRY(path)));
700     printf("setting %s/%s\n", wms_server->server, wms_server->path);
701    
702     /* set texts below */
703     gtk_label_set_text(GTK_LABEL(context->server_label), wms_server->server);
704     gtk_label_set_text(GTK_LABEL(context->path_label), wms_server->path);
705    
706     gtk_widget_destroy(dialog);
707    
708     return TRUE;
709     }
710    
711     gtk_widget_destroy(dialog);
712     return FALSE;
713     }
714    
715     /* user clicked "edit..." button in the wms server list */
716     static void on_server_edit(GtkWidget *but, wms_server_context_t *context) {
717     GtkTreeSelection *selection;
718     GtkTreeModel *model;
719     GtkTreeIter iter;
720    
721     selection = gtk_tree_view_get_selection(GTK_TREE_VIEW(context->view));
722     if(gtk_tree_selection_get_selected(selection, &model, &iter)) {
723     wms_server_t *server = NULL;
724     gtk_tree_model_get(model, &iter, WMS_SERVER_COL_DATA, &server, -1);
725     g_assert(server);
726    
727     wms_server_edit(context, FALSE, server);
728     }
729     }
730    
731     /* user clicked "add..." button in the wms server list */
732     static void on_server_add(GtkWidget *but, wms_server_context_t *context) {
733    
734     /* attach a new server item to the chain */
735     wms_server_t **prev = &context->appdata->settings->wms_server;
736     while(*prev) prev = &(*prev)->next;
737    
738     *prev = g_new0(wms_server_t, 1);
739     (*prev)->name = g_strdup("<service name>");
740     (*prev)->server = g_strdup("<server url>");
741     (*prev)->path = g_strdup("<path in server>");
742    
743     GtkTreeModel *model =
744     gtk_tree_view_get_model(GTK_TREE_VIEW(context->view));
745    
746     GtkTreeIter iter;
747     gtk_list_store_append(GTK_LIST_STORE(model), &iter);
748     gtk_list_store_set(GTK_LIST_STORE(model), &iter,
749     WMS_SERVER_COL_NAME, (*prev)->name,
750     WMS_SERVER_COL_DATA, *prev,
751     -1);
752    
753     GtkTreeSelection *selection =
754     gtk_tree_view_get_selection(GTK_TREE_VIEW(context->view));
755     gtk_tree_selection_select_iter(selection, &iter);
756    
757     if(!wms_server_edit(context, TRUE, *prev)) {
758     /* user has cancelled request. remove newly added item */
759     printf("user clicked cancel\n");
760    
761     wms_server_free(*prev);
762     *prev = NULL;
763    
764     gtk_list_store_remove(GTK_LIST_STORE(model), &iter);
765     } else
766     /* update name from edit result */
767     gtk_list_store_set(GTK_LIST_STORE(model), &iter,
768     WMS_SERVER_COL_NAME, (*prev)->name,
769     -1);
770    
771     wms_server_selected(context, *prev);
772     }
773    
774     /* widget to select a wms server from a list */
775     static GtkWidget *wms_server_widget(wms_server_context_t *context) {
776    
777     GtkWidget *vbox = gtk_vbox_new(FALSE,3);
778     context->view = gtk_tree_view_new();
779    
780     gtk_tree_selection_set_select_function(
781     gtk_tree_view_get_selection(GTK_TREE_VIEW(context->view)),
782     wms_server_selection_func,
783     context, NULL);
784    
785     /* --- "Name" column --- */
786     GtkCellRenderer *renderer = gtk_cell_renderer_text_new();
787     gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(context->view),
788     -1, _("Name"), renderer, "text", WMS_SERVER_COL_NAME, NULL);
789    
790    
791     /* build and fill the store */
792     context->store = gtk_list_store_new(WMS_SERVER_NUM_COLS,
793     G_TYPE_STRING, G_TYPE_POINTER);
794    
795     gtk_tree_view_set_model(GTK_TREE_VIEW(context->view),
796     GTK_TREE_MODEL(context->store));
797    
798     GtkTreeIter iter;
799     wms_server_t *wms_server = context->appdata->settings->wms_server;
800     while(wms_server) {
801     /* Append a row and fill in some data */
802     gtk_list_store_append(context->store, &iter);
803     gtk_list_store_set(context->store, &iter,
804     WMS_SERVER_COL_NAME, wms_server->name,
805     WMS_SERVER_COL_DATA, wms_server,
806     -1);
807    
808     wms_server = wms_server->next;
809     }
810    
811     g_object_unref(context->store);
812    
813     /* put it into a scrolled window */
814     GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
815     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
816     GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
817     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window),
818     GTK_SHADOW_ETCHED_IN);
819     gtk_container_add(GTK_CONTAINER(scrolled_window), context->view);
820    
821     gtk_box_pack_start_defaults(GTK_BOX(vbox), scrolled_window);
822    
823     /* ------- button box ------------ */
824    
825     GtkWidget *hbox = gtk_hbox_new(TRUE,3);
826    
827     context->but_add = gtk_button_new_with_label(_("Add..."));
828     gtk_box_pack_start_defaults(GTK_BOX(hbox), context->but_add);
829     gtk_signal_connect(GTK_OBJECT(context->but_add), "clicked",
830     GTK_SIGNAL_FUNC(on_server_add), context);
831    
832     context->but_edit = gtk_button_new_with_label(_("Edit..."));
833     gtk_box_pack_start_defaults(GTK_BOX(hbox), context->but_edit);
834     gtk_signal_connect(GTK_OBJECT(context->but_edit), "clicked",
835     GTK_SIGNAL_FUNC(on_server_edit), context);
836    
837     context->but_remove = gtk_button_new_with_label(_("Remove"));
838     gtk_box_pack_start_defaults(GTK_BOX(hbox), context->but_remove);
839     gtk_signal_connect(GTK_OBJECT(context->but_remove), "clicked",
840     GTK_SIGNAL_FUNC(on_server_remove), context);
841    
842     gtk_box_pack_start(GTK_BOX(vbox), hbox, FALSE, FALSE, 0);
843     return vbox;
844     }
845    
846 harbaum 1 static gboolean wms_server_dialog(appdata_t *appdata, wms_t *wms) {
847     gboolean ok = FALSE;
848    
849 harbaum 14 wms_server_context_t *context = g_new0(wms_server_context_t, 1);
850     context->appdata = appdata;
851     context->wms = wms;
852    
853     context->dialog = gtk_dialog_new_with_buttons(_("WMS Server Selection"),
854 harbaum 1 GTK_WINDOW(appdata->window), GTK_DIALOG_MODAL,
855     GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
856     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
857     NULL);
858    
859     #ifdef USE_HILDON
860 harbaum 14 gtk_window_set_default_size(GTK_WINDOW(context->dialog), 500, 300);
861 harbaum 1 #else
862 harbaum 14 gtk_window_set_default_size(GTK_WINDOW(context->dialog), 400, 200);
863 harbaum 1 #endif
864    
865 harbaum 14 /* server selection box */
866     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),
867     wms_server_widget(context));
868    
869     gtk_box_pack_start(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),
870     gtk_hseparator_new(), FALSE, FALSE, 0);
871    
872 harbaum 1 GtkWidget *label;
873     GtkWidget *table = gtk_table_new(2, 2, FALSE); // x, y
874 harbaum 14 gtk_table_set_col_spacing(GTK_TABLE(table), 0, 10);
875 harbaum 1
876     label = gtk_label_new(_("Server:"));
877 harbaum 14 gtk_misc_set_alignment(GTK_MISC(label), 0.f, 0.5f);
878     gtk_table_attach(GTK_TABLE(table), label, 0, 1, 0, 1, GTK_FILL, 0,0,0);
879     context->server_label = gtk_label_new("");
880     gtk_label_set_ellipsize(GTK_LABEL(context->server_label),
881     PANGO_ELLIPSIZE_MIDDLE);
882     gtk_misc_set_alignment(GTK_MISC(context->server_label), 0.f, 0.5f);
883     gtk_table_attach_defaults(GTK_TABLE(table), context->server_label,
884     1, 2, 0, 1);
885 harbaum 1
886     label = gtk_label_new(_("Path:"));
887 harbaum 14 gtk_misc_set_alignment(GTK_MISC(label), 0.f, 0.5f);
888     gtk_table_attach(GTK_TABLE(table), label, 0, 1, 1, 2, GTK_FILL, 0,0,0);
889     context->path_label = gtk_label_new("");
890     gtk_label_set_ellipsize(GTK_LABEL(context->path_label),
891     PANGO_ELLIPSIZE_MIDDLE);
892     gtk_misc_set_alignment(GTK_MISC(context->path_label), 0.f, 0.5f);
893     gtk_table_attach_defaults(GTK_TABLE(table), context->path_label,
894     1, 2, 1, 2);
895 harbaum 1
896 harbaum 14 gtk_box_pack_start(GTK_BOX(GTK_DIALOG(context->dialog)->vbox),
897     table, FALSE, FALSE, 0);
898    
899    
900     wms_server_selected(context, NULL);
901    
902     gtk_widget_show_all(context->dialog);
903 harbaum 1
904 harbaum 14 if(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(context->dialog))) {
905     wms_server_t *server = get_selection(context);
906     if(server) {
907     /* fetch parameters from selected entry */
908     printf("WMS: using %s\n", server->name);
909     if(wms->server) g_free(wms->server);
910     wms->server = g_strdup(server->server);
911     if(wms->path) g_free(wms->path);
912     wms->path = g_strdup(server->path);
913     ok = TRUE;
914     } else {
915     if(wms->server && wms->path)
916     ok = TRUE;
917    
918     }
919 harbaum 1 }
920    
921 harbaum 14 gtk_widget_destroy(context->dialog);
922 harbaum 1
923 harbaum 14 g_free(context);
924 harbaum 1 return ok;
925     }
926    
927     enum {
928     LAYER_COL_TITLE = 0,
929     LAYER_COL_SELECTED,
930     LAYER_COL_FITS,
931     LAYER_COL_DATA,
932     LAYER_NUM_COLS
933     };
934    
935     static void
936     layer_toggled(GtkCellRendererToggle *cell, const gchar *path_str,
937     GtkListStore *store) {
938     GtkTreePath *path;
939     GtkTreeIter iter;
940    
941     path = gtk_tree_path_new_from_string(path_str);
942     gtk_tree_model_get_iter(GTK_TREE_MODEL(store), &iter, path);
943    
944     /* get current enabled flag */
945     gboolean enabled;
946     gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
947     LAYER_COL_SELECTED, &enabled, -1);
948    
949     /* change it and store it */
950     enabled = !enabled;
951     gtk_list_store_set(store, &iter, LAYER_COL_SELECTED, enabled, -1);
952    
953     /* and store it in the layer itself */
954     wms_layer_t *layer = NULL;
955     gtk_tree_model_get(GTK_TREE_MODEL(store), &iter, LAYER_COL_DATA, &layer, -1);
956     layer->selected = enabled;
957    
958     /* walk the entire store to get all values */
959     if(gtk_tree_model_get_iter_first(GTK_TREE_MODEL(store), &iter)) {
960     gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
961     LAYER_COL_SELECTED, &enabled, -1);
962    
963     while(gtk_tree_model_iter_next(GTK_TREE_MODEL(store), &iter) &&
964     !enabled)
965     gtk_tree_model_get(GTK_TREE_MODEL(store), &iter,
966     LAYER_COL_SELECTED, &enabled, -1);
967     }
968    
969     GtkWidget *dialog = g_object_get_data(G_OBJECT(store), "dialog");
970     gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog),
971     GTK_RESPONSE_ACCEPT, enabled);
972    
973     gtk_tree_path_free(path);
974     }
975    
976     static GtkWidget *wms_layer_widget(appdata_t *appdata, wms_layer_t *layer,
977     GtkWidget *dialog) {
978     GtkWidget *view = gtk_tree_view_new();
979    
980     /* build the store */
981     GtkListStore *store = gtk_list_store_new(LAYER_NUM_COLS,
982     G_TYPE_STRING, G_TYPE_BOOLEAN, G_TYPE_BOOLEAN, G_TYPE_POINTER);
983    
984     g_object_set_data(G_OBJECT(store), "dialog", dialog);
985    
986     /* --- "selected" column --- */
987     GtkCellRenderer *renderer = gtk_cell_renderer_toggle_new();
988     g_signal_connect(renderer, "toggled", G_CALLBACK(layer_toggled), store);
989     gtk_tree_view_insert_column_with_attributes(GTK_TREE_VIEW(view),
990     -1, _(""), renderer,
991     "active", LAYER_COL_SELECTED,
992     "activatable", LAYER_COL_FITS,
993     NULL);
994    
995     /* --- "Title" column --- */
996     renderer = gtk_cell_renderer_text_new();
997     g_object_set(renderer, "ellipsize", PANGO_ELLIPSIZE_END, NULL );
998     GtkTreeViewColumn *column = gtk_tree_view_column_new_with_attributes(
999     _("Title"), renderer,
1000     "text", LAYER_COL_TITLE,
1001     NULL);
1002    
1003     gtk_tree_view_column_set_expand(column, TRUE);
1004     gtk_tree_view_insert_column(GTK_TREE_VIEW(view), column, -1);
1005    
1006     gtk_tree_view_set_model(GTK_TREE_VIEW(view), GTK_TREE_MODEL(store));
1007    
1008     GtkTreeIter iter;
1009     while(layer) {
1010     gboolean fits = wms_llbbox_fits(appdata->project, &layer->llbbox);
1011    
1012     /* Append a row and fill in some data */
1013     gtk_list_store_append(store, &iter);
1014     gtk_list_store_set(store, &iter,
1015     LAYER_COL_SELECTED, FALSE,
1016     LAYER_COL_TITLE, layer->title,
1017     LAYER_COL_FITS, fits,
1018     LAYER_COL_DATA, layer,
1019     -1);
1020     layer = layer->next;
1021     }
1022    
1023     g_object_unref(store);
1024    
1025     /* put it into a scrolled window */
1026     GtkWidget *scrolled_window = gtk_scrolled_window_new(NULL, NULL);
1027     gtk_scrolled_window_set_policy(GTK_SCROLLED_WINDOW(scrolled_window),
1028     GTK_POLICY_NEVER, GTK_POLICY_AUTOMATIC);
1029     gtk_scrolled_window_set_shadow_type(GTK_SCROLLED_WINDOW(scrolled_window),
1030     GTK_SHADOW_ETCHED_IN);
1031     gtk_container_add(GTK_CONTAINER(scrolled_window), view);
1032    
1033     return scrolled_window;
1034     }
1035    
1036    
1037     static gboolean wms_layer_dialog(appdata_t *appdata, wms_layer_t *layer) {
1038     gboolean ok = FALSE;
1039    
1040     GtkWidget *dialog = gtk_dialog_new_with_buttons(_("WMS layer selection"),
1041     GTK_WINDOW(appdata->window), GTK_DIALOG_MODAL,
1042     GTK_STOCK_CANCEL, GTK_RESPONSE_REJECT,
1043     GTK_STOCK_OK, GTK_RESPONSE_ACCEPT,
1044     NULL);
1045    
1046     gtk_dialog_set_response_sensitive(GTK_DIALOG(dialog),
1047     GTK_RESPONSE_ACCEPT, FALSE);
1048    
1049     #ifdef USE_HILDON
1050     /* making the dialog a little wider makes it less "crowded" */
1051     gtk_window_set_default_size(GTK_WINDOW(dialog), 640, 300);
1052     #else
1053     gtk_window_set_default_size(GTK_WINDOW(dialog), 400, 200);
1054     #endif
1055    
1056     /* layer list */
1057     gtk_box_pack_start_defaults(GTK_BOX(GTK_DIALOG(dialog)->vbox),
1058     wms_layer_widget(appdata, layer, dialog));
1059    
1060    
1061     gtk_widget_show_all(dialog);
1062    
1063     if(GTK_RESPONSE_ACCEPT == gtk_dialog_run(GTK_DIALOG(dialog)))
1064     ok = TRUE;
1065    
1066     gtk_widget_destroy(dialog);
1067    
1068     return ok;
1069     }
1070    
1071     static gboolean wms_one_layer_is_usable(project_t *project,
1072     wms_layer_t *layer) {
1073     gboolean ok = FALSE;
1074    
1075     while(layer) {
1076     if(layer->name && layer->epsg4326 && layer->llbbox.valid)
1077     ok = TRUE;
1078    
1079     printf("----- Layer \"%s\" -----\n", layer->title);
1080     printf("Name: %s\n", layer->name);
1081     printf("epsg4326: %s\n", layer->epsg4326?"yes":"no");
1082     if(layer->llbbox.valid) {
1083     printf("llbbox: %f/%f %f/%f\n",
1084     layer->llbbox.min.lat, layer->llbbox.min.lon,
1085     layer->llbbox.max.lat, layer->llbbox.max.lon);
1086    
1087     printf("llbbox fits project: %s\n",
1088     wms_llbbox_fits(project, &layer->llbbox)?"yes":"no");
1089     } else
1090     printf("llbbox: none/invalid\n");
1091    
1092     layer = layer->next;
1093     }
1094    
1095     return ok;
1096     }
1097    
1098     void wms_import(appdata_t *appdata) {
1099     if(!appdata->project) {
1100     errorf(GTK_WIDGET(appdata->window),
1101     _("Need an open project to derive WMS coordinates"));
1102     return;
1103     }
1104    
1105     /* this cancels any wms adjustment in progress */
1106     if(appdata->map->action.type == MAP_ACTION_BG_ADJUST)
1107     map_action_cancel(appdata);
1108    
1109     wms_t *wms = g_new0(wms_t,1);
1110     wms->server = g_strdup(appdata->project->wms_server);
1111     wms->path = g_strdup(appdata->project->wms_path);
1112    
1113     /* reset any background adjustments in the project ... */
1114     if((appdata->project->wms_offset.x != 0)||
1115     (appdata->project->wms_offset.y != 0)) {
1116    
1117     appdata->project->wms_offset.x = 0;
1118     appdata->project->wms_offset.y = 0;
1119     appdata->project->dirty = TRUE;
1120     }
1121    
1122     /* ... as well as in the map */
1123     appdata->map->bg.offset.x = 0;
1124     appdata->map->bg.offset.y = 0;
1125    
1126     /* get server from dialog */
1127     if(!wms_server_dialog(appdata, wms)) {
1128     wms_free(wms);
1129     return;
1130     }
1131    
1132     /* ------------- copy values back into project ---------------- */
1133     if(strcmp(appdata->project->wms_server, wms->server) != 0) {
1134     g_free(appdata->project->wms_server);
1135     appdata->project->wms_server = g_strdup(wms->server);
1136     appdata->project->dirty = TRUE;
1137     }
1138    
1139     if(strcmp(appdata->project->wms_path, wms->path) != 0) {
1140     g_free(appdata->project->wms_path);
1141     appdata->project->wms_path = g_strdup(wms->path);
1142     appdata->project->dirty = TRUE;
1143     }
1144    
1145     /* ----------- request capabilities -------------- */
1146     gboolean path_contains_qm = (strchr(wms->path, '?') != NULL);
1147     gboolean path_ends_with_special =
1148     (wms->path[strlen(wms->path)-1] == '?') ||
1149     (wms->path[strlen(wms->path)-1] == '&');
1150    
1151     /* if there's already a question mark, then add further */
1152     /* parameters using the &, else use the ? */
1153     char *append_char = path_ends_with_special?"":(path_contains_qm?"&":"?");
1154    
1155     char *url = g_strdup_printf("%s%s"
1156     "%sSERVICE=wms"
1157 harbaum 14 // "&WMTVER=1.1.1"
1158     "&VERSION=1.1.1"
1159 harbaum 1 "&REQUEST=GetCapabilities",
1160     wms->server, wms->path, append_char);
1161    
1162 harbaum 7 char *cap = NULL;
1163     net_io_download_mem(GTK_WIDGET(appdata->window), url, &cap);
1164 harbaum 1 g_free(url);
1165    
1166     /* ----------- parse capabilities -------------- */
1167    
1168     if(!cap) {
1169     errorf(GTK_WIDGET(appdata->window),
1170     _("WMS download failed:\n\n"
1171     "GetCapabilities failed"));
1172     } else {
1173     xmlDoc *doc = NULL;
1174    
1175     LIBXML_TEST_VERSION;
1176    
1177     /* parse the file and get the DOM */
1178     if((doc = xmlReadMemory(cap, strlen(cap), NULL, NULL, 0)) == NULL) {
1179     xmlErrorPtr errP = xmlGetLastError();
1180     errorf(GTK_WIDGET(appdata->window),
1181     _("WMS download failed:\n\n"
1182     "XML error while parsing capabilities:\n"
1183     "%s"), errP->message);
1184     } else {
1185     printf("ok, parse doc tree\n");
1186    
1187     wms_cap_parse_doc(wms, doc);
1188     }
1189    
1190     g_free(cap);
1191     }
1192    
1193     /* ------------ basic checks ------------- */
1194    
1195     if(!wms->cap || !wms->service || !wms->cap->layer ||
1196     !wms->cap->request || !wms->cap->request->getmap) {
1197     errorf(GTK_WIDGET(appdata->window), _("Incomplete/unexpected reply!"));
1198     wms_free(wms);
1199     return;
1200     }
1201    
1202     if(!wms->cap->request->getmap->format) {
1203     errorf(GTK_WIDGET(appdata->window), _("No supported image format found."));
1204     wms_free(wms);
1205     return;
1206     }
1207    
1208     /* ---------- evaluate layers ----------- */
1209    
1210     wms_layer_t *layer = wms_get_requestable_layers(wms);
1211     gboolean at_least_one_ok = wms_one_layer_is_usable(appdata->project, layer);
1212    
1213     if(!at_least_one_ok) {
1214     errorf(GTK_WIDGET(appdata->window),
1215     _("Server provides no data in the required format!\n"
1216     "(epsg4326 and LatLonBoundingBox are mandatory for osm2go)"));
1217     wms_layer_free(layer);
1218     wms_free(wms);
1219     return;
1220     }
1221    
1222     if(!wms_layer_dialog(appdata, layer)) {
1223     wms_layer_free(layer);
1224     wms_free(wms);
1225     return;
1226     }
1227    
1228     /* --------- build getmap request ----------- */
1229    
1230     /* get required image size */
1231     wms_setup_extent(appdata->project, wms);
1232    
1233     /* start building url */
1234     url = g_strdup_printf("%s%s"
1235     "%sSERVICE=wms"
1236 harbaum 14 // "&WMTVER=1.1.1"
1237     "&VERSION=1.1.1"
1238 harbaum 1 "&REQUEST=GetMap"
1239     "&LAYERS=",
1240     wms->server, wms->path, append_char);
1241    
1242     /* append layers */
1243     char *old;
1244     wms_layer_t *t = layer;
1245     gint cnt = 0;
1246     while(t) {
1247     if(t->selected) {
1248     old = url;
1249     url = g_strconcat(url, (!cnt)?"":",", t->name, NULL);
1250     g_free(old);
1251     cnt++;
1252     }
1253     t = t->next;
1254     }
1255     wms_layer_free(layer);
1256    
1257     /* append styles entry */
1258     old = url;
1259     url = g_strconcat(url, "&STYLES=", NULL);
1260     g_free(old);
1261    
1262     while(--cnt) {
1263     old = url;
1264     url = g_strconcat(url, ",", NULL);
1265     g_free(old);
1266     }
1267    
1268     /* and append rest */
1269     char minlon[G_ASCII_DTOSTR_BUF_SIZE], minlat[G_ASCII_DTOSTR_BUF_SIZE];
1270     char maxlon[G_ASCII_DTOSTR_BUF_SIZE], maxlat[G_ASCII_DTOSTR_BUF_SIZE];
1271    
1272 harbaum 14 /* build strings of min and max lat and lon to be used in url */
1273 harbaum 1 g_ascii_dtostr(minlon, sizeof(minlon), appdata->project->min.lon);
1274     g_ascii_dtostr(minlat, sizeof(minlat), appdata->project->min.lat);
1275     g_ascii_dtostr(maxlon, sizeof(maxlon), appdata->project->max.lon);
1276     g_ascii_dtostr(maxlat, sizeof(maxlat), appdata->project->max.lat);
1277    
1278 harbaum 14 /* find preferred supported video format */
1279 harbaum 1 gint format = 0;
1280     while(!(wms->cap->request->getmap->format & (1<<format)))
1281     format++;
1282    
1283     const char *formats[] = { "image/jpg", "image/jpeg",
1284     "image/png", "image/gif" };
1285    
1286 harbaum 14 /* build complete url */
1287 harbaum 1 old = url;
1288     url = g_strdup_printf("%s&SRS=EPSG:4326&BBOX=%s,%s,%s,%s"
1289 harbaum 14 "&WIDTH=%d&HEIGHT=%d&FORMAT=%s"
1290     "&reaspect=false", url,
1291 harbaum 1 minlon, minlat, maxlon, maxlat, wms->width,
1292     wms->height, formats[format]);
1293     g_free(old);
1294    
1295     const char *exts[] = { "jpg", "jpg", "png", "gif" };
1296     char *filename = g_strdup_printf("%s/wms.%s", appdata->project->path,
1297     exts[format]);
1298    
1299    
1300     /* remove any existing image before */
1301     wms_remove(appdata);
1302    
1303 harbaum 4 if(!net_io_download_file(GTK_WIDGET(appdata->window), url, filename)) {
1304 harbaum 1 g_free(filename);
1305     g_free(url);
1306     wms_free(wms);
1307     return;
1308     }
1309    
1310     /* there should be a matching file on disk now */
1311     map_set_bg_image(appdata->map, filename);
1312    
1313     g_free(filename);
1314     g_free(url);
1315    
1316     /* --------- free wms structure -----------------*/
1317     wms_free(wms);
1318    
1319     gtk_widget_set_sensitive(appdata->menu_item_wms_clear, TRUE);
1320     gtk_widget_set_sensitive(appdata->menu_item_wms_adjust, TRUE);
1321     }
1322    
1323     /* try to load an existing image into map */
1324     void wms_load(appdata_t *appdata) {
1325     const char *exts[] = { "png", "gif", "jpg", "" };
1326     int i=0;
1327    
1328     while(exts[i][0]) {
1329     char *filename = g_strdup_printf("%s/wms.%s", appdata->project->path,
1330     exts[i]);
1331    
1332     if(g_file_test(filename, G_FILE_TEST_EXISTS)) {
1333     appdata->map->bg.offset.x = appdata->project->wms_offset.x;
1334     appdata->map->bg.offset.y = appdata->project->wms_offset.y;
1335    
1336     map_set_bg_image(appdata->map, filename);
1337    
1338     /* restore image to saved position */
1339     gint x = appdata->osm->bounds->min.x + appdata->map->bg.offset.x;
1340     gint y = appdata->osm->bounds->min.y + appdata->map->bg.offset.y;
1341     canvas_image_move(appdata->map->bg.item, x, y,
1342     appdata->map->bg.scale.x, appdata->map->bg.scale.y);
1343    
1344     g_free(filename);
1345    
1346     gtk_widget_set_sensitive(appdata->menu_item_wms_clear, TRUE);
1347     gtk_widget_set_sensitive(appdata->menu_item_wms_adjust, TRUE);
1348    
1349     return;
1350     }
1351     g_free(filename);
1352     i++;
1353     }
1354     }
1355    
1356     void wms_remove(appdata_t *appdata) {
1357     const char *exts[] = { "png", "gif", "jpg", "" };
1358     int i=0;
1359    
1360     /* this cancels any wms adjustment in progress */
1361     if(appdata->map->action.type == MAP_ACTION_BG_ADJUST)
1362     map_action_cancel(appdata);
1363    
1364     gtk_widget_set_sensitive(appdata->menu_item_wms_clear, FALSE);
1365     gtk_widget_set_sensitive(appdata->menu_item_wms_adjust, FALSE);
1366    
1367     map_remove_bg_image(appdata->map);
1368    
1369     while(exts[i][0]) {
1370     char *filename = g_strdup_printf("%s/wms.%s", appdata->project->path,
1371     exts[i]);
1372    
1373     if(g_file_test(filename, G_FILE_TEST_EXISTS))
1374     g_remove(filename);
1375    
1376     g_free(filename);
1377     i++;
1378     }
1379     }
1380    
1381 harbaum 14 struct server_preset_s {
1382     char *name, *server, *path;
1383     } default_servers[] = {
1384     { "NASA landsat", "http://onearth.jpl.nasa.gov", "/wms.cgi" },
1385     /* add more servers here ... */
1386     { NULL, NULL, NULL }
1387     };
1388    
1389     wms_server_t *wms_server_get_default(void) {
1390     wms_server_t *server = NULL, **cur = &server;
1391     struct server_preset_s *preset = default_servers;
1392    
1393     while(preset->name) {
1394     *cur = g_new0(wms_server_t, 1);
1395     (*cur)->name = g_strdup(preset->name);
1396     (*cur)->server = g_strdup(preset->server);
1397     (*cur)->path = g_strdup(preset->path);
1398     cur = &(*cur)->next;
1399     preset++;
1400     }
1401    
1402     return server;
1403     }
1404    
1405     void wms_server_free(wms_server_t *wms_server) {
1406     if(wms_server->name) g_free(wms_server->name);
1407     if(wms_server->server) g_free(wms_server->server);
1408     if(wms_server->path) g_free(wms_server->path);
1409     g_free(wms_server);
1410     }
1411    
1412     void wms_servers_free(wms_server_t *wms_server) {
1413     while(wms_server) {
1414     wms_server_t *next = wms_server->next;
1415     wms_server_free(wms_server);
1416     wms_server = next;
1417     }
1418     }