]> git.street.me.uk Git - andy/viking.git/blob - src/vikgeoreflayer.c
Prevent lock up in attempt to download maps along a track in UTM mode.
[andy/viking.git] / src / vikgeoreflayer.c
1 /*
2  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3  *
4  * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5  *
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.
10  *
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.
15  *
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
19  *
20  */
21
22 #ifdef HAVE_CONFIG_H
23 #include "config.h"
24 #endif
25
26 #include "viking.h"
27 #include <glib.h>
28 #include <glib/gstdio.h>
29 #include <glib/gi18n.h>
30 #include <string.h>
31 #include <math.h>
32 #include <stdlib.h>
33
34 #include "icons/icons.h"
35
36 VikLayerParam georef_layer_params[] = {
37   { "image", VIK_LAYER_PARAM_STRING, VIK_LAYER_NOT_IN_PROPERTIES },
38   { "corner_easting", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_NOT_IN_PROPERTIES },
39   { "corner_northing", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_NOT_IN_PROPERTIES },
40   { "mpp_easting", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_NOT_IN_PROPERTIES },
41   { "mpp_northing", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_NOT_IN_PROPERTIES },
42 };
43
44 enum { PARAM_IMAGE = 0, PARAM_CE, PARAM_CN, PARAM_ME, PARAM_MN, NUM_PARAMS };
45
46 static void georef_layer_marshall( VikGeorefLayer *vgl, guint8 **data, gint *len );
47 static VikGeorefLayer *georef_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
48 static gboolean georef_layer_set_param ( VikGeorefLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp );
49 static VikLayerParamData georef_layer_get_param ( VikGeorefLayer *vgl, guint16 id );
50 VikGeorefLayer *georef_layer_new ( );
51 VikGeorefLayer *georef_layer_create ( VikViewport *vp );
52 static void georef_layer_free ( VikGeorefLayer *vgl );
53 gboolean georef_layer_properties ( VikGeorefLayer *vgl, gpointer vp );
54 static void georef_layer_draw ( VikGeorefLayer *vgl, gpointer data );
55 static void georef_layer_add_menu_items ( VikGeorefLayer *vgl, GtkMenu *menu, gpointer vlp );
56 static void georef_layer_set_image ( VikGeorefLayer *vgl, const gchar *image );
57 static gboolean georef_layer_dialog ( VikGeorefLayer **vgl, gpointer vp, GtkWindow *w );
58 static void georef_layer_load_image ( VikGeorefLayer *vgl );
59
60 /* tools */
61 static gpointer georef_layer_move_create ( VikWindow *vw, VikViewport *vvp);
62 static gboolean georef_layer_move_release ( VikGeorefLayer *vgl, GdkEventButton *event, VikViewport *vvp );
63 static gboolean georef_layer_move_press ( VikGeorefLayer *vgl, GdkEventButton *event, VikViewport *vvp );
64 static gpointer georef_layer_zoom_create ( VikWindow *vw, VikViewport *vvp);
65 static gboolean georef_layer_zoom_press ( VikGeorefLayer *vgl, GdkEventButton *event, VikViewport *vvp );
66
67 static VikToolInterface georef_tools[] = {
68   { N_("Georef Move Map"), (VikToolConstructorFunc) georef_layer_move_create, NULL, NULL, NULL,
69     (VikToolMouseFunc) georef_layer_move_press, NULL, (VikToolMouseFunc) georef_layer_move_release,
70     (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_geomove_pixbuf },
71
72   { N_("Georef Zoom Tool"), (VikToolConstructorFunc) georef_layer_zoom_create, NULL, NULL, NULL,
73     (VikToolMouseFunc) georef_layer_zoom_press, NULL, NULL,
74     (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_geozoom_pixbuf },
75 };
76
77 VikLayerInterface vik_georef_layer_interface = {
78   "GeoRef Map",
79   &vikgeoreflayer_pixbuf, /*icon */
80
81   georef_tools,
82   sizeof(georef_tools) / sizeof(VikToolInterface),
83
84   georef_layer_params,
85   NUM_PARAMS,
86   NULL,
87   0,
88
89   VIK_MENU_ITEM_ALL,
90
91   (VikLayerFuncCreate)                  georef_layer_create,
92   (VikLayerFuncRealize)                 NULL,
93   (VikLayerFuncPostRead)                georef_layer_load_image,
94   (VikLayerFuncFree)                    georef_layer_free,
95
96   (VikLayerFuncProperties)              georef_layer_properties,
97   (VikLayerFuncDraw)                    georef_layer_draw,
98   (VikLayerFuncChangeCoordMode)         NULL,
99
100   (VikLayerFuncSetMenuItemsSelection)   NULL,
101   (VikLayerFuncGetMenuItemsSelection)   NULL,
102
103   (VikLayerFuncAddMenuItems)            georef_layer_add_menu_items,
104   (VikLayerFuncSublayerAddMenuItems)    NULL,
105
106   (VikLayerFuncSublayerRenameRequest)   NULL,
107   (VikLayerFuncSublayerToggleVisible)   NULL,
108
109   (VikLayerFuncMarshall)                georef_layer_marshall,
110   (VikLayerFuncUnmarshall)              georef_layer_unmarshall,
111
112   (VikLayerFuncSetParam)                georef_layer_set_param,
113   (VikLayerFuncGetParam)                georef_layer_get_param,
114
115   (VikLayerFuncReadFileData)            NULL,
116   (VikLayerFuncWriteFileData)           NULL,
117
118   (VikLayerFuncDeleteItem)              NULL,
119   (VikLayerFuncCopyItem)                NULL,
120   (VikLayerFuncPasteItem)               NULL,
121   (VikLayerFuncFreeCopiedItem)          NULL,
122   (VikLayerFuncDragDropRequest)         NULL,
123 };
124
125 struct _VikGeorefLayer {
126   VikLayer vl;
127   gchar *image;
128   GdkPixbuf *pixbuf;
129   struct UTM corner;
130   gdouble mpp_easting, mpp_northing;
131   guint width, height;
132
133   GdkPixbuf *scaled;
134   guint32 scaled_width, scaled_height;
135
136   gint click_x, click_y;
137 };
138
139
140
141 GType vik_georef_layer_get_type ()
142 {
143   static GType vgl_type = 0;
144
145   if (!vgl_type)
146   {
147     static const GTypeInfo vgl_info =
148     {
149       sizeof (VikGeorefLayerClass),
150       NULL, /* base_init */
151       NULL, /* base_finalize */
152       NULL, /* class init */
153       NULL, /* class_finalize */
154       NULL, /* class_data */
155       sizeof (VikGeorefLayer),
156       0,
157       NULL /* instance init */
158     };
159     vgl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikGeorefLayer", &vgl_info, 0 );
160   }
161
162   return vgl_type;
163 }
164
165 static void georef_layer_marshall( VikGeorefLayer *vgl, guint8 **data, gint *len )
166 {
167   vik_layer_marshall_params ( VIK_LAYER(vgl), data, len );
168 }
169
170 static VikGeorefLayer *georef_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
171 {
172   VikGeorefLayer *rv = georef_layer_new ( vvp );
173   vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
174   if (rv->image) {
175     georef_layer_load_image ( rv );
176   }
177   return rv;
178 }
179
180 static gboolean georef_layer_set_param ( VikGeorefLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp )
181 {
182   switch ( id )
183   {
184     case PARAM_IMAGE: georef_layer_set_image ( vgl, data.s ); break;
185     case PARAM_CN: vgl->corner.northing = data.d; break;
186     case PARAM_CE: vgl->corner.easting = data.d; break;
187     case PARAM_MN: vgl->mpp_northing = data.d; break;
188     case PARAM_ME:  vgl->mpp_easting = data.d; break;
189   }
190   return TRUE;
191 }
192
193 static VikLayerParamData georef_layer_get_param ( VikGeorefLayer *vgl, guint16 id )
194 {
195   VikLayerParamData rv;
196   switch ( id )
197   {
198     case PARAM_IMAGE: rv.s = vgl->image ? vgl->image : ""; break;
199     case PARAM_CN: rv.d = vgl->corner.northing; break;
200     case PARAM_CE: rv.d = vgl->corner.easting; break;
201     case PARAM_MN: rv.d = vgl->mpp_northing; break;
202     case PARAM_ME: rv.d = vgl->mpp_easting; break;
203   }
204   return rv;
205 }
206
207 VikGeorefLayer *georef_layer_new ( )
208 {
209   VikGeorefLayer *vgl = VIK_GEOREF_LAYER ( g_object_new ( VIK_GEOREF_LAYER_TYPE, NULL ) );
210   vik_layer_init ( VIK_LAYER(vgl), VIK_LAYER_GEOREF );
211
212   vgl->image = NULL;
213   vgl->pixbuf = NULL;
214   vgl->click_x = -1;
215   vgl->click_y = -1;
216   vgl->scaled = NULL;
217   vgl->scaled_width = 0;
218   vgl->scaled_height = 0;
219   return vgl;
220 }
221
222 static void georef_layer_draw ( VikGeorefLayer *vgl, gpointer data )
223 {
224 /* bla, bla */
225   if ( vgl->pixbuf )
226   {
227     VikViewport *vp = VIK_VIEWPORT(data);
228     struct UTM utm_middle;
229     gdouble xmpp = vik_viewport_get_xmpp(vp), ympp = vik_viewport_get_ympp(vp);
230     GdkPixbuf *pixbuf = vgl->pixbuf;
231     guint layer_width = vgl->width;
232     guint layer_height = vgl->height;
233
234     vik_coord_to_utm ( vik_viewport_get_center ( vp ), &utm_middle );
235
236     /* scale the pixbuf if it doesn't match our dimensions */
237     if ( xmpp != vgl->mpp_easting || ympp != vgl->mpp_northing )
238     {
239       layer_width = round(vgl->width * vgl->mpp_easting / xmpp);
240       layer_height = round(vgl->height * vgl->mpp_northing / ympp);
241
242       /* rescale if necessary */
243       if (layer_width == vgl->scaled_width && layer_height == vgl->scaled_height && vgl->scaled != NULL)
244         pixbuf = vgl->scaled;
245       else
246       {
247         pixbuf = gdk_pixbuf_scale_simple(
248           vgl->pixbuf, 
249           layer_width,
250           layer_height,
251           GDK_INTERP_BILINEAR
252         );
253
254         if (vgl->scaled != NULL)
255           g_object_unref(vgl->scaled);
256
257         vgl->scaled = pixbuf;
258         vgl->scaled_width = layer_width;
259         vgl->scaled_height = layer_height;
260       }
261     }
262
263     guint width = vik_viewport_get_width(vp), height = vik_viewport_get_height(vp);
264     gint32 x, y;
265     vgl->corner.zone = utm_middle.zone;
266     vgl->corner.letter = utm_middle.letter;
267     VikCoord corner_coord;
268     vik_coord_load_from_utm ( &corner_coord, vik_viewport_get_coord_mode(vp), &(vgl->corner) );
269     vik_viewport_coord_to_screen ( vp, &corner_coord, &x, &y );
270     if ( (x < 0 || x < width) && (y < 0 || y < height) && x+layer_width > 0 && y+layer_height > 0 )
271       vik_viewport_draw_pixbuf ( vp, pixbuf, 0, 0, x, y, layer_width, layer_height ); /* todo: draw only what we need to. */
272   }
273 }
274
275 static void georef_layer_free ( VikGeorefLayer *vgl )
276 {
277   if ( vgl->image != NULL )
278     g_free ( vgl->image );
279   if ( vgl->scaled != NULL )
280     g_object_unref ( vgl->scaled );
281 }
282
283 VikGeorefLayer *georef_layer_create ( VikViewport *vp )
284 {
285   return georef_layer_new ();
286 }
287
288 gboolean georef_layer_properties ( VikGeorefLayer *vgl, gpointer vp )
289 {
290   return georef_layer_dialog ( &vgl, vp, VIK_GTK_WINDOW_FROM_WIDGET(vp) );
291 }
292
293 static void georef_layer_load_image ( VikGeorefLayer *vgl )
294 {
295   GError *gx = NULL;
296   if ( vgl->image == NULL )
297     return;
298
299   if ( vgl->pixbuf )
300     g_object_unref ( G_OBJECT(vgl->pixbuf) );
301   if ( vgl->scaled )
302   {
303     g_object_unref ( G_OBJECT(vgl->scaled) );
304     vgl->scaled = NULL;
305   }
306
307   vgl->pixbuf = gdk_pixbuf_new_from_file ( vgl->image, &gx );
308
309   if (gx)
310   {
311     g_warning ( _("Couldn't open image file: %s"), gx->message );
312     g_error_free ( gx );
313   }
314   else
315   {
316     vgl->width = gdk_pixbuf_get_width ( vgl->pixbuf );
317     vgl->height = gdk_pixbuf_get_height ( vgl->pixbuf );
318   }
319
320   /* should find length and width here too */
321 }
322
323 static void georef_layer_set_image ( VikGeorefLayer *vgl, const gchar *image )
324 {
325   if ( vgl->image )
326     g_free ( vgl->image );
327   if ( vgl->scaled )
328   {
329     g_object_unref ( vgl->scaled );
330     vgl->scaled = NULL;
331   }
332   if ( image == NULL )
333     vgl->image = NULL;
334   vgl->image = g_strdup ( image );
335 }
336
337 static gboolean world_file_read_line ( gchar *buffer, gint size, FILE *f, GtkWidget *widget, gboolean use_value )
338 {
339   if (!fgets ( buffer, 1024, f ))
340   {
341     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_WIDGET(widget), _("Unexpected end of file reading World file.") );
342     g_free ( buffer );
343     fclose ( f );
344     f = NULL;
345     return FALSE;
346   }
347   if ( use_value )
348   {
349     gdouble val = g_strtod ( buffer, NULL );
350     gtk_spin_button_set_value ( GTK_SPIN_BUTTON(widget), val > 0 ? val : -val );
351   }
352   return TRUE;
353 }
354
355 static void georef_layer_dialog_load ( GtkWidget *pass_along[4] )
356 {
357   GtkWidget *file_selector = gtk_file_chooser_dialog_new (_("Choose World file"),
358                                       NULL,
359                                       GTK_FILE_CHOOSER_ACTION_OPEN,
360                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
361                                       GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
362                                       NULL);
363
364   if ( gtk_dialog_run ( GTK_DIALOG ( file_selector ) ) == GTK_RESPONSE_ACCEPT )
365   {
366     FILE *f = g_fopen ( gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(file_selector) ), "r" );
367     gtk_widget_destroy ( file_selector ); 
368     if ( !f )
369     {
370       a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_WIDGET(pass_along[0]), _("The World file you requested could not be opened for reading.") );
371       return;
372     }
373     else
374     {
375       gchar *buffer = g_malloc ( 1024 * sizeof(gchar) );
376       if ( world_file_read_line ( buffer, 1024, f, pass_along[0], TRUE ) && world_file_read_line ( buffer, 1024, f, pass_along[0], FALSE)
377         && world_file_read_line ( buffer, 1024, f, pass_along[0], FALSE ) && world_file_read_line ( buffer, 1024, f, pass_along[1], TRUE)
378         && world_file_read_line ( buffer, 1024, f, pass_along[2], TRUE ) && world_file_read_line ( buffer, 1024, f, pass_along[3], TRUE ) )
379       {
380         g_free ( buffer );
381         fclose ( f );
382         f = NULL;
383       }
384     }
385   }
386   else
387     gtk_widget_destroy ( file_selector ); 
388 /* do your jazz
389 We need a
390   file selection dialog
391   file opener for reading, if NULL, send error_msg ( VIK_GTK_WINDOW_FROM_WIDGET(pass_along[0]) )
392   does that catch directories too?
393   read lines -- if not enough lines, give error.
394   if anything outside, give error. define range with #define CONSTANTS
395   put 'em in thar widgets, and that's it.
396 */
397 }
398
399 static void georef_layer_export_params ( gpointer *pass_along[2] )
400 {
401   VikGeorefLayer *vgl = VIK_GEOREF_LAYER(pass_along[0]);
402   GtkWidget *file_selector = gtk_file_chooser_dialog_new (_("Choose World file"),
403                                       NULL,
404                                       GTK_FILE_CHOOSER_ACTION_SAVE,
405                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
406                                       GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
407                                       NULL);
408   if ( gtk_dialog_run ( GTK_DIALOG ( file_selector ) ) == GTK_RESPONSE_ACCEPT )
409   {
410     FILE *f = g_fopen ( gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(file_selector) ), "w" );
411     
412     gtk_widget_destroy ( file_selector ); 
413     if ( !f )
414     {
415       a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_WIDGET(pass_along[0]), _("The file you requested could not be opened for writing.") );
416       return;
417     }
418     else
419     {
420       fprintf ( f, "%f\n%f\n%f\n%f\n%f\n%f\n", vgl->mpp_easting, vgl->mpp_northing, 0.0, 0.0, vgl->corner.easting, vgl->corner.northing );
421       fclose ( f );
422       f = NULL;
423     }
424   }
425   else
426    gtk_widget_destroy ( file_selector ); 
427 }
428
429 /* returns TRUE if OK was pressed. */
430 static gboolean georef_layer_dialog ( VikGeorefLayer **vgl, gpointer vp, GtkWindow *w )
431 {
432   GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Layer Properties"),
433                                                   w,
434                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
435                                                   GTK_STOCK_CANCEL,
436                                                   GTK_RESPONSE_REJECT,
437                                                   GTK_STOCK_OK,
438                                                   GTK_RESPONSE_ACCEPT,
439                                                   NULL );
440   GtkWidget *table, *wfp_hbox, *wfp_label, *wfp_button, *ce_label, *ce_spin, *cn_label, *cn_spin, *xlabel, *xspin, *ylabel, *yspin, *imagelabel, *imageentry;
441
442   GtkWidget *pass_along[4];
443
444   table = gtk_table_new ( 6, 2, FALSE );
445   gtk_box_pack_start ( GTK_BOX(GTK_DIALOG(dialog)->vbox), table, TRUE, TRUE, 0 );
446
447   wfp_hbox = gtk_hbox_new ( FALSE, 0 );
448   wfp_label = gtk_label_new ( _("World File Parameters:") );
449   wfp_button = gtk_button_new_with_label ( _("Load From File...") );
450
451   gtk_box_pack_start ( GTK_BOX(wfp_hbox), wfp_label, TRUE, TRUE, 0 );
452   gtk_box_pack_start ( GTK_BOX(wfp_hbox), wfp_button, FALSE, FALSE, 3 );
453
454   ce_label = gtk_label_new ( _("Corner pixel easting:") );
455   ce_spin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new ( 4, 0.0, 1500000.0, 1, 5, 0 ), 1, 4 );
456   gtk_widget_set_tooltip_text ( GTK_WIDGET(ce_spin), _("the UTM \"easting\" value of the upper-right corner pixel of the map") );
457
458   cn_label = gtk_label_new ( _("Corner pixel northing:") );
459   cn_spin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new ( 4, 0.0, 9000000.0, 1, 5, 0 ), 1, 4 );
460   gtk_widget_set_tooltip_text ( GTK_WIDGET(cn_spin), _("the UTM \"northing\" value of the upper-right corner pixel of the map") );
461
462   xlabel = gtk_label_new ( _("X (easting) scale (mpp): "));
463   ylabel = gtk_label_new ( _("Y (northing) scale (mpp): "));
464
465   xspin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new ( 4, VIK_VIEWPORT_MIN_ZOOM, VIK_VIEWPORT_MAX_ZOOM, 1, 5, 0 ), 1, 8 );
466   gtk_widget_set_tooltip_text ( GTK_WIDGET(xspin), _("the scale of the map in the X direction (meters per pixel)") );
467   yspin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new ( 4, VIK_VIEWPORT_MIN_ZOOM, VIK_VIEWPORT_MAX_ZOOM, 1, 5, 0 ), 1, 8 );
468   gtk_widget_set_tooltip_text ( GTK_WIDGET(yspin), _("the scale of the map in the Y direction (meters per pixel)") );
469
470   imagelabel = gtk_label_new ( _("Map Image:") );
471   imageentry = vik_file_entry_new (GTK_FILE_CHOOSER_ACTION_OPEN);
472
473   if (*vgl)
474   {
475     gtk_spin_button_set_value ( GTK_SPIN_BUTTON(ce_spin), (*vgl)->corner.easting );
476     gtk_spin_button_set_value ( GTK_SPIN_BUTTON(cn_spin), (*vgl)->corner.northing );
477     gtk_spin_button_set_value ( GTK_SPIN_BUTTON(xspin), (*vgl)->mpp_easting );
478     gtk_spin_button_set_value ( GTK_SPIN_BUTTON(yspin), (*vgl)->mpp_northing );
479     if ( (*vgl)->image )
480     vik_file_entry_set_filename ( VIK_FILE_ENTRY(imageentry), (*vgl)->image );
481   }
482   else
483   {
484     VikCoord corner_coord;
485     struct UTM utm;
486     vik_viewport_screen_to_coord ( VIK_VIEWPORT(vp), 0, 0, &corner_coord );
487     vik_coord_to_utm ( &corner_coord, &utm );
488     gtk_spin_button_set_value ( GTK_SPIN_BUTTON(ce_spin), utm.easting );
489     gtk_spin_button_set_value ( GTK_SPIN_BUTTON(cn_spin), utm.northing );
490     gtk_spin_button_set_value ( GTK_SPIN_BUTTON(xspin), vik_viewport_get_xmpp ( VIK_VIEWPORT(vp) ) );
491     gtk_spin_button_set_value ( GTK_SPIN_BUTTON(yspin), vik_viewport_get_ympp ( VIK_VIEWPORT(vp) ) );
492   }
493
494   gtk_table_attach_defaults ( GTK_TABLE(table), imagelabel, 0, 1, 0, 1 );
495   gtk_table_attach_defaults ( GTK_TABLE(table), imageentry, 1, 2, 0, 1 );
496   gtk_table_attach_defaults ( GTK_TABLE(table), wfp_hbox, 0, 2, 1, 2 );
497   gtk_table_attach_defaults ( GTK_TABLE(table), xlabel, 0, 1, 2, 3 );
498   gtk_table_attach_defaults ( GTK_TABLE(table), xspin, 1, 2, 2, 3 );
499   gtk_table_attach_defaults ( GTK_TABLE(table), ylabel, 0, 1, 3, 4 );
500   gtk_table_attach_defaults ( GTK_TABLE(table), yspin, 1, 2, 3, 4 );
501   gtk_table_attach_defaults ( GTK_TABLE(table), ce_label, 0, 1, 4, 5 );
502   gtk_table_attach_defaults ( GTK_TABLE(table), ce_spin, 1, 2, 4, 5 );
503   gtk_table_attach_defaults ( GTK_TABLE(table), cn_label, 0, 1, 5, 6 );
504   gtk_table_attach_defaults ( GTK_TABLE(table), cn_spin, 1, 2, 5, 6 );
505
506   pass_along[0] = xspin;
507   pass_along[1] = yspin;
508   pass_along[2] = ce_spin;
509   pass_along[3] = cn_spin;
510   g_signal_connect_swapped ( G_OBJECT(wfp_button), "clicked", G_CALLBACK(georef_layer_dialog_load), pass_along );
511
512   gtk_widget_show_all ( table );
513
514   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
515   {
516     if (! *vgl)
517     {
518       *vgl = georef_layer_new ();
519        vik_layer_rename ( VIK_LAYER(*vgl), vik_georef_layer_interface.name );
520     }
521     (*vgl)->corner.easting = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(ce_spin) );
522     (*vgl)->corner.northing = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(cn_spin) );
523     (*vgl)->mpp_easting = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(xspin) );
524     (*vgl)->mpp_northing = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(yspin) );
525     if ( (!(*vgl)->image) || strcmp( (*vgl)->image, vik_file_entry_get_filename(VIK_FILE_ENTRY(imageentry)) ) != 0 )
526     {
527       georef_layer_set_image ( *vgl, vik_file_entry_get_filename(VIK_FILE_ENTRY(imageentry)) );
528       georef_layer_load_image ( *vgl );
529     }
530
531     gtk_widget_destroy ( GTK_WIDGET(dialog) );
532     return TRUE;
533   }
534   gtk_widget_destroy ( GTK_WIDGET(dialog) );
535   return FALSE;
536 }
537
538 static void georef_layer_zoom_to_fit ( gpointer vgl_vlp[2] )
539 {
540   vik_viewport_set_xmpp ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vgl_vlp[1])), VIK_GEOREF_LAYER(vgl_vlp[0])->mpp_easting );
541   vik_viewport_set_ympp ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vgl_vlp[1])), VIK_GEOREF_LAYER(vgl_vlp[0])->mpp_northing );
542   vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vgl_vlp[1]) );
543 }
544
545 static void georef_layer_goto_center ( gpointer vgl_vlp[2] )
546 {
547   VikGeorefLayer *vgl = VIK_GEOREF_LAYER ( vgl_vlp[0] );
548   VikViewport *vp = vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vgl_vlp[1]));
549   struct UTM utm;
550   VikCoord coord;
551
552   vik_coord_to_utm ( vik_viewport_get_center ( vp ), &utm );
553
554   utm.easting = vgl->corner.easting + (vgl->width * vgl->mpp_easting / 2); /* only an approximation */
555   utm.northing = vgl->corner.northing - (vgl->height * vgl->mpp_northing / 2);
556
557   vik_coord_load_from_utm ( &coord, vik_viewport_get_coord_mode ( vp ), &utm );
558   vik_viewport_set_center_coord ( vp, &coord );
559
560   vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vgl_vlp[1]) );
561 }
562
563 static void georef_layer_add_menu_items ( VikGeorefLayer *vgl, GtkMenu *menu, gpointer vlp )
564 {
565   static gpointer pass_along[2];
566   GtkWidget *item;
567   pass_along[0] = vgl;
568   pass_along[1] = vlp;
569
570   item = gtk_menu_item_new();
571   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
572   gtk_widget_show ( item );
573
574   item = gtk_menu_item_new_with_label ( _("Zoom to Fit Map") );
575   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(georef_layer_zoom_to_fit), pass_along );
576   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
577   gtk_widget_show ( item );
578
579   item = gtk_menu_item_new_with_label ( _("Goto Map Center") );
580   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(georef_layer_goto_center), pass_along );
581   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
582   gtk_widget_show ( item );
583
584   item = gtk_menu_item_new_with_label ( _("Export to World File") );
585   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(georef_layer_export_params), pass_along );
586   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
587   gtk_widget_show ( item );
588 }
589
590
591 static gpointer georef_layer_move_create ( VikWindow *vw, VikViewport *vvp)
592 {
593   return vvp;
594 }
595
596 static gboolean georef_layer_move_release ( VikGeorefLayer *vgl, GdkEventButton *event, VikViewport *vvp )
597 {
598   if (!vgl || vgl->vl.type != VIK_LAYER_GEOREF)
599     return FALSE;
600
601   if ( vgl->click_x != -1 )
602   {
603     vgl->corner.easting += (event->x - vgl->click_x) * vik_viewport_get_xmpp (vvp);
604     vgl->corner.northing -= (event->y - vgl->click_y) * vik_viewport_get_ympp (vvp);
605     vik_layer_emit_update ( VIK_LAYER(vgl) );
606     return TRUE;
607   }
608   return FALSE; /* I didn't move anything on this layer! */
609 }
610
611 static gpointer georef_layer_zoom_create ( VikWindow *vw, VikViewport *vvp)
612 {
613   return vvp;
614 }
615
616 static gboolean georef_layer_zoom_press ( VikGeorefLayer *vgl, GdkEventButton *event, VikViewport *vvp )
617 {
618   if (!vgl || vgl->vl.type != VIK_LAYER_GEOREF)
619     return FALSE;
620   if ( event->button == 1 )
621   {
622     if ( vgl->mpp_easting < (VIK_VIEWPORT_MAX_ZOOM / 1.05) && vgl->mpp_northing < (VIK_VIEWPORT_MAX_ZOOM / 1.05) )
623     {
624       vgl->mpp_easting *= 1.01;
625       vgl->mpp_northing *= 1.01;
626     }
627   }
628   else
629   {
630     if ( vgl->mpp_easting > (VIK_VIEWPORT_MIN_ZOOM * 1.05) && vgl->mpp_northing > (VIK_VIEWPORT_MIN_ZOOM * 1.05) )
631     {
632       vgl->mpp_easting /= 1.01;
633       vgl->mpp_northing /= 1.01;
634     }
635   }
636   vik_viewport_set_xmpp ( vvp, vgl->mpp_easting );
637   vik_viewport_set_ympp ( vvp, vgl->mpp_northing );
638   vik_layer_emit_update ( VIK_LAYER(vgl) );
639   return TRUE;
640 }
641
642 static gboolean georef_layer_move_press ( VikGeorefLayer *vgl, GdkEventButton *event, VikViewport *vvp )
643 {
644   if (!vgl || vgl->vl.type != VIK_LAYER_GEOREF)
645     return FALSE;
646   vgl->click_x = event->x;
647   vgl->click_y = event->y;
648   return TRUE;
649 }