]> git.street.me.uk Git - andy/viking.git/blob - src/vikgeoreflayer.c
Merge branch 'GeoRefImprove'
[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  * Copyright (c) 2014, Rob Norris <rw_norris@hotmail.com>
6  *
7  * This program is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU General Public License as published by
9  * the Free Software Foundation; either version 2 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20  *
21  */
22
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26
27 #include "viking.h"
28 #include "vikutils.h"
29 #include <glib.h>
30 #include <glib/gstdio.h>
31 #include <glib/gi18n.h>
32 #include <string.h>
33 #include <math.h>
34 #include <stdlib.h>
35 #include <ctype.h>
36
37 #include "ui_util.h"
38 #include "preferences.h"
39 #include "icons/icons.h"
40 /*
41 static VikLayerParamData image_default ( void )
42 {
43   VikLayerParamData data;
44   data.s = g_strdup ("");
45   return data;
46 }
47 */
48
49 VikLayerParam georef_layer_params[] = {
50   { VIK_LAYER_GEOREF, "image", VIK_LAYER_PARAM_STRING, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL },
51   { VIK_LAYER_GEOREF, "corner_easting", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL },
52   { VIK_LAYER_GEOREF, "corner_northing", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL },
53   { VIK_LAYER_GEOREF, "mpp_easting", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL },
54   { VIK_LAYER_GEOREF, "mpp_northing", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL },
55   { VIK_LAYER_GEOREF, "corner_zone", VIK_LAYER_PARAM_UINT, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL },
56   { VIK_LAYER_GEOREF, "corner_letter_as_int", VIK_LAYER_PARAM_UINT, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL },
57   { VIK_LAYER_GEOREF, "alpha", VIK_LAYER_PARAM_UINT, VIK_LAYER_NOT_IN_PROPERTIES, NULL, 0, NULL, NULL, NULL, NULL, NULL, NULL },
58 };
59
60 enum {
61   PARAM_IMAGE = 0,
62   PARAM_CE,
63   PARAM_CN,
64   PARAM_ME,
65   PARAM_MN,
66   PARAM_CZ,
67   PARAM_CL,
68   PARAM_AA,
69   NUM_PARAMS };
70
71 static const gchar* georef_layer_tooltip ( VikGeorefLayer *vgl );
72 static void georef_layer_marshall( VikGeorefLayer *vgl, guint8 **data, gint *len );
73 static VikGeorefLayer *georef_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
74 static gboolean georef_layer_set_param ( VikGeorefLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
75 static VikLayerParamData georef_layer_get_param ( VikGeorefLayer *vgl, guint16 id, gboolean is_file_operation );
76 static VikGeorefLayer *georef_layer_new ( VikViewport *vvp );
77 static VikGeorefLayer *georef_layer_create ( VikViewport *vp );
78 static void georef_layer_free ( VikGeorefLayer *vgl );
79 static gboolean georef_layer_properties ( VikGeorefLayer *vgl, gpointer vp );
80 static void georef_layer_draw ( VikGeorefLayer *vgl, VikViewport *vp );
81 static void georef_layer_add_menu_items ( VikGeorefLayer *vgl, GtkMenu *menu, gpointer vlp );
82 static void georef_layer_set_image ( VikGeorefLayer *vgl, const gchar *image );
83 static gboolean georef_layer_dialog ( VikGeorefLayer *vgl, gpointer vp, GtkWindow *w );
84 static void georef_layer_load_image ( VikGeorefLayer *vgl, VikViewport *vp, gboolean from_file );
85
86 /* tools */
87 static gpointer georef_layer_move_create ( VikWindow *vw, VikViewport *vvp);
88 static gboolean georef_layer_move_release ( VikGeorefLayer *vgl, GdkEventButton *event, VikViewport *vvp );
89 static gboolean georef_layer_move_press ( VikGeorefLayer *vgl, GdkEventButton *event, VikViewport *vvp );
90 static gpointer georef_layer_zoom_create ( VikWindow *vw, VikViewport *vvp);
91 static gboolean georef_layer_zoom_press ( VikGeorefLayer *vgl, GdkEventButton *event, VikViewport *vvp );
92
93 // See comment in viktrwlayer.c for advice on values used
94 static VikToolInterface georef_tools[] = {
95   { { "GeorefMoveMap", "vik-icon-Georef Move Map",  N_("_Georef Move Map"), NULL,  N_("Georef Move Map"), 0 },
96     (VikToolConstructorFunc) georef_layer_move_create, NULL, NULL, NULL,
97     (VikToolMouseFunc) georef_layer_move_press, NULL, (VikToolMouseFunc) georef_layer_move_release,
98     (VikToolKeyFunc) NULL,
99     FALSE,
100     GDK_CURSOR_IS_PIXMAP, &cursor_geomove_pixbuf, NULL },
101
102   { { "GeorefZoomTool", "vik-icon-Georef Zoom Tool",  N_("Georef Z_oom Tool"), NULL,  N_("Georef Zoom Tool"), 0 },
103     (VikToolConstructorFunc) georef_layer_zoom_create, NULL, NULL, NULL,
104     (VikToolMouseFunc) georef_layer_zoom_press, NULL, NULL,
105     (VikToolKeyFunc) NULL,
106     FALSE,
107     GDK_CURSOR_IS_PIXMAP, &cursor_geozoom_pixbuf, NULL },
108 };
109
110 VikLayerInterface vik_georef_layer_interface = {
111   "GeoRef Map",
112   N_("GeoRef Map"),
113   NULL,
114   &vikgeoreflayer_pixbuf, /*icon */
115
116   georef_tools,
117   sizeof(georef_tools) / sizeof(VikToolInterface),
118
119   georef_layer_params,
120   NUM_PARAMS,
121   NULL,
122   0,
123
124   VIK_MENU_ITEM_ALL,
125
126   (VikLayerFuncCreate)                  georef_layer_create,
127   (VikLayerFuncRealize)                 NULL,
128   (VikLayerFuncPostRead)                georef_layer_load_image,
129   (VikLayerFuncFree)                    georef_layer_free,
130
131   (VikLayerFuncProperties)              georef_layer_properties,
132   (VikLayerFuncDraw)                    georef_layer_draw,
133   (VikLayerFuncChangeCoordMode)         NULL,
134
135   (VikLayerFuncSetMenuItemsSelection)   NULL,
136   (VikLayerFuncGetMenuItemsSelection)   NULL,
137
138   (VikLayerFuncAddMenuItems)            georef_layer_add_menu_items,
139   (VikLayerFuncSublayerAddMenuItems)    NULL,
140
141   (VikLayerFuncSublayerRenameRequest)   NULL,
142   (VikLayerFuncSublayerToggleVisible)   NULL,
143   (VikLayerFuncSublayerTooltip)         NULL,
144   (VikLayerFuncLayerTooltip)            georef_layer_tooltip,
145   (VikLayerFuncLayerSelected)           NULL,
146
147   (VikLayerFuncMarshall)                georef_layer_marshall,
148   (VikLayerFuncUnmarshall)              georef_layer_unmarshall,
149
150   (VikLayerFuncSetParam)                georef_layer_set_param,
151   (VikLayerFuncGetParam)                georef_layer_get_param,
152   (VikLayerFuncChangeParam)             NULL,
153
154   (VikLayerFuncReadFileData)            NULL,
155   (VikLayerFuncWriteFileData)           NULL,
156
157   (VikLayerFuncDeleteItem)              NULL,
158   (VikLayerFuncCutItem)                 NULL,
159   (VikLayerFuncCopyItem)                NULL,
160   (VikLayerFuncPasteItem)               NULL,
161   (VikLayerFuncFreeCopiedItem)          NULL,
162   (VikLayerFuncDragDropRequest)         NULL,
163
164   (VikLayerFuncSelectClick)             NULL,
165   (VikLayerFuncSelectMove)              NULL,
166   (VikLayerFuncSelectRelease)           NULL,
167   (VikLayerFuncSelectedViewportMenu)    NULL,
168 };
169
170 typedef struct {
171   GtkWidget *x_spin;
172   GtkWidget *y_spin;
173   // UTM widgets
174   GtkWidget *ce_spin; // top left
175   GtkWidget *cn_spin; //    "
176   GtkWidget *utm_zone_spin;
177   GtkWidget *utm_letter_entry;
178
179   GtkWidget *lat_tl_spin;
180   GtkWidget *lon_tl_spin;
181   GtkWidget *lat_br_spin;
182   GtkWidget *lon_br_spin;
183   //
184   GtkWidget *tabs;
185   GtkWidget *imageentry;
186 } changeable_widgets;
187
188 struct _VikGeorefLayer {
189   VikLayer vl;
190   gchar *image;
191   GdkPixbuf *pixbuf;
192   guint8 alpha;
193
194   struct UTM corner; // Top Left
195   gdouble mpp_easting, mpp_northing;
196   struct LatLon ll_br; // Bottom Right
197   guint width, height;
198
199   GdkPixbuf *scaled;
200   guint32 scaled_width, scaled_height;
201
202   gint click_x, click_y;
203   changeable_widgets cw;
204 };
205
206 static VikLayerParam io_prefs[] = {
207   { VIK_LAYER_NUM_TYPES, VIKING_PREFERENCES_IO_NAMESPACE "georef_auto_read_world_file", VIK_LAYER_PARAM_BOOLEAN, VIK_LAYER_GROUP_NONE, N_("Auto Read World Files:"), VIK_LAYER_WIDGET_CHECKBUTTON, NULL, NULL,
208     N_("Automatically attempt to read associated world file of a new image for a GeoRef layer"), NULL, NULL, NULL}
209 };
210
211 void vik_georef_layer_init (void)
212 {
213   VikLayerParamData tmp;
214   tmp.b = TRUE;
215   a_preferences_register(&io_prefs[0], tmp, VIKING_PREFERENCES_IO_GROUP_KEY);
216 }
217
218 GType vik_georef_layer_get_type ()
219 {
220   static GType vgl_type = 0;
221
222   if (!vgl_type)
223   {
224     static const GTypeInfo vgl_info =
225     {
226       sizeof (VikGeorefLayerClass),
227       NULL, /* base_init */
228       NULL, /* base_finalize */
229       NULL, /* class init */
230       NULL, /* class_finalize */
231       NULL, /* class_data */
232       sizeof (VikGeorefLayer),
233       0,
234       NULL /* instance init */
235     };
236     vgl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikGeorefLayer", &vgl_info, 0 );
237   }
238
239   return vgl_type;
240 }
241
242 static const gchar* georef_layer_tooltip ( VikGeorefLayer *vgl )
243 {
244   return vgl->image;
245 }
246
247 static void georef_layer_marshall( VikGeorefLayer *vgl, guint8 **data, gint *len )
248 {
249   vik_layer_marshall_params ( VIK_LAYER(vgl), data, len );
250 }
251
252 static VikGeorefLayer *georef_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
253 {
254   VikGeorefLayer *rv = georef_layer_new ( vvp );
255   vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
256   if (rv->image) {
257     georef_layer_load_image ( rv, vvp, TRUE );
258   }
259   return rv;
260 }
261
262 static gboolean georef_layer_set_param ( VikGeorefLayer *vgl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
263 {
264   switch ( id )
265   {
266     case PARAM_IMAGE: georef_layer_set_image ( vgl, data.s ); break;
267     case PARAM_CN: vgl->corner.northing = data.d; break;
268     case PARAM_CE: vgl->corner.easting = data.d; break;
269     case PARAM_MN: vgl->mpp_northing = data.d; break;
270     case PARAM_ME: vgl->mpp_easting = data.d; break;
271     case PARAM_CZ: if ( data.u <= 60 ) vgl->corner.zone = data.u; break;
272     case PARAM_CL: if ( data.u >= 65 || data.u <= 90 ) vgl->corner.letter = data.u; break;
273     case PARAM_AA: if ( data.u <= 255 ) vgl->alpha = data.u; break;
274     default: break;
275   }
276   return TRUE;
277 }
278
279 static VikLayerParamData georef_layer_get_param ( VikGeorefLayer *vgl, guint16 id, gboolean is_file_operation )
280 {
281   VikLayerParamData rv;
282   switch ( id )
283   {
284     case PARAM_IMAGE: {
285       gboolean set = FALSE;
286       if ( is_file_operation ) {
287         if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE ) {
288           gchar *cwd = g_get_current_dir();
289           if ( cwd ) {
290             rv.s = file_GetRelativeFilename ( cwd, vgl->image );
291             if ( !rv.s ) rv.s = "";
292             set = TRUE;
293           }
294         }
295       }
296       if ( !set )
297         rv.s = vgl->image ? vgl->image : "";
298       break;
299     }
300     case PARAM_CN: rv.d = vgl->corner.northing; break;
301     case PARAM_CE: rv.d = vgl->corner.easting; break;
302     case PARAM_MN: rv.d = vgl->mpp_northing; break;
303     case PARAM_ME: rv.d = vgl->mpp_easting; break;
304     case PARAM_CZ: rv.u = vgl->corner.zone; break;
305     case PARAM_CL: rv.u = vgl->corner.letter; break;
306     case PARAM_AA: rv.u = vgl->alpha; break;
307     default: break;
308   }
309   return rv;
310 }
311
312 static VikGeorefLayer *georef_layer_new ( VikViewport *vvp )
313 {
314   VikGeorefLayer *vgl = VIK_GEOREF_LAYER ( g_object_new ( VIK_GEOREF_LAYER_TYPE, NULL ) );
315   vik_layer_set_type ( VIK_LAYER(vgl), VIK_LAYER_GEOREF );
316
317   // Since GeoRef layer doesn't use uibuilder
318   //  initializing this way won't do anything yet..
319   vik_layer_set_defaults ( VIK_LAYER(vgl), vvp );
320
321   // Make these defaults based on the current view
322   vgl->mpp_northing = vik_viewport_get_ympp ( vvp );
323   vgl->mpp_easting = vik_viewport_get_xmpp ( vvp );
324   vik_coord_to_utm ( vik_viewport_get_center ( vvp ), &(vgl->corner) );
325
326   vgl->image = NULL;
327   vgl->pixbuf = NULL;
328   vgl->click_x = -1;
329   vgl->click_y = -1;
330   vgl->scaled = NULL;
331   vgl->scaled_width = 0;
332   vgl->scaled_height = 0;
333   vgl->ll_br.lat = 0.0;
334   vgl->ll_br.lon = 0.0;
335   vgl->alpha = 255;
336   return vgl;
337 }
338
339 static void georef_layer_draw ( VikGeorefLayer *vgl, VikViewport *vp )
340 {
341   if ( vgl->pixbuf )
342   {
343     gdouble xmpp = vik_viewport_get_xmpp(vp), ympp = vik_viewport_get_ympp(vp);
344     GdkPixbuf *pixbuf = vgl->pixbuf;
345     guint layer_width = vgl->width;
346     guint layer_height = vgl->height;
347
348     guint width = vik_viewport_get_width(vp), height = vik_viewport_get_height(vp);
349     gint32 x, y;
350     VikCoord corner_coord;
351     vik_coord_load_from_utm ( &corner_coord, vik_viewport_get_coord_mode(vp), &(vgl->corner) );
352     vik_viewport_coord_to_screen ( vp, &corner_coord, &x, &y );
353
354     /* mark to scale the pixbuf if it doesn't match our dimensions */
355     gboolean scale = FALSE;
356     if ( xmpp != vgl->mpp_easting || ympp != vgl->mpp_northing )
357     {
358       scale = TRUE;
359       layer_width = round(vgl->width * vgl->mpp_easting / xmpp);
360       layer_height = round(vgl->height * vgl->mpp_northing / ympp);
361     }
362
363     // If image not in viewport bounds - no need to draw it (or bother with any scaling)
364     if ( (x < 0 || x < width) && (y < 0 || y < height) && x+layer_width > 0 && y+layer_height > 0 ) {
365
366       if ( scale )
367       {
368         /* rescale if necessary */
369         if (layer_width == vgl->scaled_width && layer_height == vgl->scaled_height && vgl->scaled != NULL)
370           pixbuf = vgl->scaled;
371         else
372         {
373           pixbuf = gdk_pixbuf_scale_simple(
374             vgl->pixbuf,
375             layer_width,
376             layer_height,
377             GDK_INTERP_BILINEAR
378           );
379
380           if (vgl->scaled != NULL)
381             g_object_unref(vgl->scaled);
382
383           vgl->scaled = pixbuf;
384           vgl->scaled_width = layer_width;
385           vgl->scaled_height = layer_height;
386         }
387       }
388       vik_viewport_draw_pixbuf ( vp, pixbuf, 0, 0, x, y, layer_width, layer_height ); /* todo: draw only what we need to. */
389     }
390   }
391 }
392
393 static void georef_layer_free ( VikGeorefLayer *vgl )
394 {
395   if ( vgl->image != NULL )
396     g_free ( vgl->image );
397   if ( vgl->scaled != NULL )
398     g_object_unref ( vgl->scaled );
399 }
400
401 static VikGeorefLayer *georef_layer_create ( VikViewport *vp )
402 {
403   return georef_layer_new ( vp );
404 }
405
406 static gboolean georef_layer_properties ( VikGeorefLayer *vgl, gpointer vp )
407 {
408   return georef_layer_dialog ( vgl, vp, VIK_GTK_WINDOW_FROM_WIDGET(vp) );
409 }
410
411 static void georef_layer_load_image ( VikGeorefLayer *vgl, VikViewport *vp, gboolean from_file )
412 {
413   GError *gx = NULL;
414   if ( vgl->image == NULL )
415     return;
416
417   if ( vgl->pixbuf )
418     g_object_unref ( G_OBJECT(vgl->pixbuf) );
419   if ( vgl->scaled )
420   {
421     g_object_unref ( G_OBJECT(vgl->scaled) );
422     vgl->scaled = NULL;
423   }
424
425   vgl->pixbuf = gdk_pixbuf_new_from_file ( vgl->image, &gx );
426
427   if (gx)
428   {
429     if ( !from_file )
430       a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_WIDGET(vp), _("Couldn't open image file: %s"), gx->message );
431     g_error_free ( gx );
432   }
433   else
434   {
435     vgl->width = gdk_pixbuf_get_width ( vgl->pixbuf );
436     vgl->height = gdk_pixbuf_get_height ( vgl->pixbuf );
437
438     if ( vgl->pixbuf && vgl->alpha < 255 )
439       vgl->pixbuf = ui_pixbuf_set_alpha ( vgl->pixbuf, vgl->alpha );
440   }
441   /* should find length and width here too */
442 }
443
444 static void georef_layer_set_image ( VikGeorefLayer *vgl, const gchar *image )
445 {
446   if ( vgl->image )
447     g_free ( vgl->image );
448   if ( vgl->scaled )
449   {
450     g_object_unref ( vgl->scaled );
451     vgl->scaled = NULL;
452   }
453   if ( image == NULL )
454     vgl->image = NULL;
455
456   if ( g_strcmp0 (image, "") != 0 )
457     vgl->image = vu_get_canonical_filename ( VIK_LAYER(vgl), image );
458   else
459     vgl->image = g_strdup (image);
460 }
461
462 // Only positive values allowed here
463 static void gdouble2spinwidget ( GtkWidget *widget, gdouble val )
464 {
465   gtk_spin_button_set_value ( GTK_SPIN_BUTTON(widget), val > 0 ? val : -val );
466 }
467
468 static void set_widget_values ( changeable_widgets *cw, gdouble values[4] )
469 {
470   gdouble2spinwidget ( cw->x_spin, values[0] );
471   gdouble2spinwidget ( cw->y_spin, values[1] );
472   gdouble2spinwidget ( cw->ce_spin, values[2] );
473   gdouble2spinwidget ( cw->cn_spin, values[3] );
474 }
475
476 static gboolean world_file_read_line ( FILE *ff, gdouble *value, gboolean use_value )
477 {
478   gboolean answer = TRUE; // Success
479   gchar buffer[1024];
480   if ( !fgets ( buffer, 1024, ff ) ) {
481     answer = FALSE;
482   }
483   if ( answer && use_value )
484       *value = g_strtod ( buffer, NULL );
485
486   return answer;
487 }
488
489 /**
490  * http://en.wikipedia.org/wiki/World_file
491  *
492  * Note world files do not define the units and nor are the units standardized :(
493  * Currently Viking only supports:
494  *  x&y scale as meters per pixel
495  *  x&y coords as UTM eastings and northings respectively
496  */
497 static gint world_file_read_file ( const gchar* filename, gdouble values[4] )
498 {
499   g_debug ("%s - trying world file %s", __FUNCTION__, filename);
500
501   FILE *f = g_fopen ( filename, "r" );
502   if ( !f )
503     return 1;
504   else {
505     gint answer = 2; // Not enough info read yet
506     // **We do not handle 'skew' values ATM - normally they are a value of 0 anyway to align with the UTM grid
507     if ( world_file_read_line ( f, &values[0], TRUE ) // x scale
508       && world_file_read_line ( f, NULL, FALSE ) // Ignore value in y-skew line**
509       && world_file_read_line ( f, NULL, FALSE ) // Ignore value in x-skew line**
510       && world_file_read_line ( f, &values[1], TRUE ) // y scale
511       && world_file_read_line ( f, &values[2], TRUE ) // x-coordinate of the upper left pixel
512       && world_file_read_line ( f, &values[3], TRUE ) // y-coordinate of the upper left pixel
513        )
514     {
515        // Success
516        g_debug ("%s - %s - world file read success", __FUNCTION__, filename);
517        answer = 0;
518     }
519     fclose ( f );
520     return answer;
521   }
522 }
523
524 static void georef_layer_dialog_load ( changeable_widgets *cw )
525 {
526   GtkWidget *file_selector = gtk_file_chooser_dialog_new (_("Choose World file"),
527                                       NULL,
528                                       GTK_FILE_CHOOSER_ACTION_OPEN,
529                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
530                                       GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
531                                       NULL);
532
533   if ( gtk_dialog_run ( GTK_DIALOG ( file_selector ) ) == GTK_RESPONSE_ACCEPT )
534   {
535      gdouble values[4];
536      gint answer = world_file_read_file ( gtk_file_chooser_get_filename(GTK_FILE_CHOOSER(file_selector)), values );
537      if ( answer == 1 )
538        a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_WIDGET(cw->x_spin), _("The World file you requested could not be opened for reading.") );
539      else if ( answer == 2 )
540        a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_WIDGET(cw->x_spin), _("Unexpected end of file reading World file.") );
541      else
542        // NB answer should == 0 for success
543        set_widget_values ( cw, values );
544   }
545
546   gtk_widget_destroy ( file_selector );
547 }
548
549 static void georef_layer_export_params ( gpointer *pass_along[2] )
550 {
551   VikGeorefLayer *vgl = VIK_GEOREF_LAYER(pass_along[0]);
552   GtkWidget *file_selector = gtk_file_chooser_dialog_new (_("Choose World file"),
553                                       NULL,
554                                       GTK_FILE_CHOOSER_ACTION_SAVE,
555                                       GTK_STOCK_CANCEL, GTK_RESPONSE_CANCEL,
556                                       GTK_STOCK_OPEN, GTK_RESPONSE_ACCEPT,
557                                       NULL);
558   if ( gtk_dialog_run ( GTK_DIALOG ( file_selector ) ) == GTK_RESPONSE_ACCEPT )
559   {
560     FILE *f = g_fopen ( gtk_file_chooser_get_filename ( GTK_FILE_CHOOSER(file_selector) ), "w" );
561     
562     gtk_widget_destroy ( file_selector ); 
563     if ( !f )
564     {
565       a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_WIDGET(pass_along[0]), _("The file you requested could not be opened for writing.") );
566       return;
567     }
568     else
569     {
570       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 );
571       fclose ( f );
572       f = NULL;
573     }
574   }
575   else
576    gtk_widget_destroy ( file_selector ); 
577 }
578
579 /**
580  * Auto attempt to read the world file associated with the image used for the georef
581  *  Based on simple file name conventions
582  * Only attempted if the preference is on.
583  */
584 static void maybe_read_world_file ( VikFileEntry *vfe, gpointer user_data )
585 {
586   if ( a_preferences_get (VIKING_PREFERENCES_IO_NAMESPACE "georef_auto_read_world_file")->b ) {
587     const gchar* filename = vik_file_entry_get_filename(VIK_FILE_ENTRY(vfe));
588     gdouble values[4];
589     if ( filename && user_data ) {
590
591       changeable_widgets *cw = user_data;
592
593       gboolean upper = g_ascii_isupper (filename[strlen(filename)-1]);
594       gchar* filew = g_strconcat ( filename, (upper ? "W" : "w") , NULL );
595
596       if ( world_file_read_file ( filew, values ) == 0 ) {
597         set_widget_values ( cw, values );
598       }
599       else {
600         if ( strlen(filename) > 3 ) {
601           gchar* file0 = g_strndup ( filename, strlen(filename)-2 );
602           gchar* file1 = g_strdup_printf ( "%s%c%c", file0, filename[strlen(filename)-1], (upper ? 'W' : 'w')  );
603           if ( world_file_read_file ( file1, values ) == 0 ) {
604             set_widget_values ( cw, values );
605           }
606           g_free ( file1 );
607           g_free ( file0 );
608         }
609       }
610       g_free ( filew );
611     }
612   }
613 }
614
615 static struct LatLon get_ll_tl (VikGeorefLayer *vgl)
616 {
617   struct LatLon ll_tl;
618   ll_tl.lat = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(vgl->cw.lat_tl_spin) );
619   ll_tl.lon = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(vgl->cw.lon_tl_spin) );
620   return ll_tl;
621 }
622
623 static struct LatLon get_ll_br (VikGeorefLayer *vgl)
624 {
625   struct LatLon ll_br;
626   ll_br.lat = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(vgl->cw.lat_br_spin) );
627   ll_br.lon = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(vgl->cw.lon_br_spin) );
628   return ll_br;
629 }
630
631 // Align displayed UTM values with displayed Lat/Lon values
632 static void align_utm2ll (VikGeorefLayer *vgl)
633 {
634   struct LatLon ll_tl = get_ll_tl (vgl);
635
636   struct UTM utm;
637   a_coords_latlon_to_utm ( &ll_tl, &utm );
638   gtk_spin_button_set_value ( GTK_SPIN_BUTTON(vgl->cw.ce_spin), utm.easting );
639   gtk_spin_button_set_value ( GTK_SPIN_BUTTON(vgl->cw.cn_spin), utm.northing );
640
641   gchar tmp_letter[2];
642   tmp_letter[0] = utm.letter;
643   tmp_letter[1] = '\0';
644   gtk_entry_set_text ( GTK_ENTRY(vgl->cw.utm_letter_entry), tmp_letter );
645
646   gtk_spin_button_set_value ( GTK_SPIN_BUTTON(vgl->cw.utm_zone_spin), utm.zone );
647 }
648
649 // Align displayed Lat/Lon values with displayed UTM values
650 static void align_ll2utm (VikGeorefLayer *vgl)
651 {
652   struct UTM corner;
653   const gchar *letter = gtk_entry_get_text ( GTK_ENTRY(vgl->cw.utm_letter_entry) );
654   if (*letter)
655     corner.letter = toupper(*letter);
656   corner.zone = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(vgl->cw.utm_zone_spin) );
657   corner.easting = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(vgl->cw.ce_spin) );
658   corner.northing = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(vgl->cw.cn_spin) );
659
660   struct LatLon ll;
661   a_coords_utm_to_latlon ( &corner, &ll );
662   gtk_spin_button_set_value ( GTK_SPIN_BUTTON(vgl->cw.lat_tl_spin), ll.lat );
663   gtk_spin_button_set_value ( GTK_SPIN_BUTTON(vgl->cw.lon_tl_spin), ll.lon );
664 }
665
666 /**
667  * Align coordinates between tabs as the user may have changed the values
668  *   Use this before acting on the user input
669  * This is easier then trying to use the 'value-changed' signal for each individual coordinate
670  *  especiallly since it tends to end up in an infinite loop continually updating each other.
671  */
672 static void align_coords ( VikGeorefLayer *vgl )
673 {
674   if (gtk_notebook_get_current_page(GTK_NOTEBOOK(vgl->cw.tabs)) == 0)
675     align_ll2utm ( vgl );
676   else
677     align_utm2ll ( vgl );
678 }
679
680 static void switch_tab (GtkNotebook *notebook, gpointer tab, guint tab_num, gpointer user_data)
681 {
682   VikGeorefLayer *vgl = user_data;
683   if ( tab_num == 0 )
684     align_utm2ll (vgl);
685   else
686     align_ll2utm (vgl);
687 }
688
689 /**
690  *
691  */
692 static void check_br_is_good_or_msg_user ( VikGeorefLayer *vgl )
693 {
694   // if a 'blank' ll value that's alright
695   if ( vgl->ll_br.lat == 0.0 && vgl->ll_br.lon == 0.0 )
696     return;
697
698   struct LatLon ll_tl = get_ll_tl (vgl);
699   if ( ll_tl.lat < vgl->ll_br.lat || ll_tl.lon > vgl->ll_br.lon )
700     a_dialog_warning_msg ( VIK_GTK_WINDOW_FROM_LAYER(vgl), _("Lower right corner values may not be consistent with upper right values") );
701 }
702
703 /**
704  *
705  */
706 static void calculate_mpp_from_coords ( GtkWidget *ww, VikGeorefLayer *vgl )
707 {
708   const gchar* filename = vik_file_entry_get_filename(VIK_FILE_ENTRY(vgl->cw.imageentry));
709   if ( !filename ) {
710     return;
711   }
712   GError *gx = NULL;
713   GdkPixbuf *pixbuf = gdk_pixbuf_new_from_file ( filename, &gx );
714   if ( gx ) {
715     a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_WIDGET(ww), _("Couldn't open image file: %s"), gx->message );
716     g_error_free ( gx );
717     return;
718   }
719
720   guint width = gdk_pixbuf_get_width ( pixbuf );
721   guint height = gdk_pixbuf_get_height ( pixbuf );
722
723   if ( width == 0 || height == 0 ) {
724     a_dialog_error_msg_extra ( VIK_GTK_WINDOW_FROM_WIDGET(ww), _("Invalid image size: %s"), filename);
725   }
726   else {
727     align_coords ( vgl );
728
729     struct LatLon ll_tl = get_ll_tl (vgl);
730     struct LatLon ll_br = get_ll_br (vgl);
731
732     struct LatLon ll_tr;
733     ll_tr.lat = ll_tl.lat;
734     ll_tr.lon = ll_br.lon;
735
736     struct LatLon ll_bl;
737     ll_bl.lat = ll_br.lat;
738     ll_bl.lon = ll_tl.lon;
739
740     gdouble diffx = a_coords_latlon_diff ( &ll_tl, &ll_tr );
741     gdouble xmpp = diffx / width;
742
743     gdouble diffy = a_coords_latlon_diff ( &ll_tl, &ll_bl );
744     gdouble ympp = diffy / height;
745
746     gtk_spin_button_set_value ( GTK_SPIN_BUTTON(vgl->cw.x_spin), xmpp );
747     gtk_spin_button_set_value ( GTK_SPIN_BUTTON(vgl->cw.y_spin), ympp );
748
749     check_br_is_good_or_msg_user ( vgl );
750   }
751
752   g_object_unref ( G_OBJECT(pixbuf) );
753 }
754
755 #define VIK_SETTINGS_GEOREF_TAB "georef_coordinate_tab"
756
757 /* returns TRUE if OK was pressed. */
758 static gboolean georef_layer_dialog ( VikGeorefLayer *vgl, gpointer vp, GtkWindow *w )
759 {
760   GtkWidget *dialog = gtk_dialog_new_with_buttons (_("Layer Properties"),
761                                                   w,
762                                                   GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT,
763                                                   GTK_STOCK_CANCEL,
764                                                   GTK_RESPONSE_REJECT,
765                                                   GTK_STOCK_OK,
766                                                   GTK_RESPONSE_ACCEPT,
767                                                   NULL );
768   /* Default to reject as user really needs to specify map file first */
769   gtk_dialog_set_default_response ( GTK_DIALOG(dialog), GTK_RESPONSE_REJECT );
770   GtkWidget *response_w = NULL;
771 #if GTK_CHECK_VERSION (2, 20, 0)
772   response_w = gtk_dialog_get_widget_for_response ( GTK_DIALOG(dialog), GTK_RESPONSE_REJECT );
773 #endif
774   GtkWidget *table, *wfp_hbox, *wfp_label, *wfp_button, *ce_label, *cn_label, *xlabel, *ylabel, *imagelabel;
775   changeable_widgets cw;
776
777   GtkBox *dgbox = GTK_BOX(gtk_dialog_get_content_area(GTK_DIALOG(dialog)));
778   table = gtk_table_new ( 4, 2, FALSE );
779   gtk_box_pack_start ( dgbox, table, TRUE, TRUE, 0 );
780
781   wfp_hbox = gtk_hbox_new ( FALSE, 0 );
782   wfp_label = gtk_label_new ( _("World File Parameters:") );
783   wfp_button = gtk_button_new_with_label ( _("Load From File...") );
784
785   gtk_box_pack_start ( GTK_BOX(wfp_hbox), wfp_label, TRUE, TRUE, 0 );
786   gtk_box_pack_start ( GTK_BOX(wfp_hbox), wfp_button, FALSE, FALSE, 3 );
787
788   ce_label = gtk_label_new ( _("Corner pixel easting:") );
789   cw.ce_spin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new ( 4, 0.0, 1500000.0, 1, 5, 0 ), 1, 4 );
790   gtk_widget_set_tooltip_text ( GTK_WIDGET(cw.ce_spin), _("the UTM \"easting\" value of the upper-left corner pixel of the map") );
791
792   cn_label = gtk_label_new ( _("Corner pixel northing:") );
793   cw.cn_spin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new ( 4, 0.0, 9000000.0, 1, 5, 0 ), 1, 4 );
794   gtk_widget_set_tooltip_text ( GTK_WIDGET(cw.cn_spin), _("the UTM \"northing\" value of the upper-left corner pixel of the map") );
795
796   xlabel = gtk_label_new ( _("X (easting) scale (mpp): "));
797   ylabel = gtk_label_new ( _("Y (northing) scale (mpp): "));
798
799   cw.x_spin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new ( 4, VIK_VIEWPORT_MIN_ZOOM, VIK_VIEWPORT_MAX_ZOOM, 1, 5, 0 ), 1, 8 );
800   gtk_widget_set_tooltip_text ( GTK_WIDGET(cw.x_spin), _("the scale of the map in the X direction (meters per pixel)") );
801   cw.y_spin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new ( 4, VIK_VIEWPORT_MIN_ZOOM, VIK_VIEWPORT_MAX_ZOOM, 1, 5, 0 ), 1, 8 );
802   gtk_widget_set_tooltip_text ( GTK_WIDGET(cw.y_spin), _("the scale of the map in the Y direction (meters per pixel)") );
803
804   imagelabel = gtk_label_new ( _("Map Image:") );
805   cw.imageentry = vik_file_entry_new (GTK_FILE_CHOOSER_ACTION_OPEN, VF_FILTER_IMAGE, maybe_read_world_file, &cw);
806
807   gtk_spin_button_set_value ( GTK_SPIN_BUTTON(cw.ce_spin), vgl->corner.easting );
808   gtk_spin_button_set_value ( GTK_SPIN_BUTTON(cw.cn_spin), vgl->corner.northing );
809   gtk_spin_button_set_value ( GTK_SPIN_BUTTON(cw.x_spin), vgl->mpp_easting );
810   gtk_spin_button_set_value ( GTK_SPIN_BUTTON(cw.y_spin), vgl->mpp_northing );
811   if ( vgl->image )
812     vik_file_entry_set_filename ( VIK_FILE_ENTRY(cw.imageentry), vgl->image );
813
814   gtk_table_attach_defaults ( GTK_TABLE(table), imagelabel, 0, 1, 0, 1 );
815   gtk_table_attach_defaults ( GTK_TABLE(table), cw.imageentry, 1, 2, 0, 1 );
816   gtk_table_attach_defaults ( GTK_TABLE(table), wfp_hbox, 0, 2, 1, 2 );
817   gtk_table_attach_defaults ( GTK_TABLE(table), xlabel, 0, 1, 2, 3 );
818   gtk_table_attach_defaults ( GTK_TABLE(table), cw.x_spin, 1, 2, 2, 3 );
819   gtk_table_attach_defaults ( GTK_TABLE(table), ylabel, 0, 1, 3, 4 );
820   gtk_table_attach_defaults ( GTK_TABLE(table), cw.y_spin, 1, 2, 3, 4 );
821
822   cw.tabs = gtk_notebook_new();
823   GtkWidget *table_utm = gtk_table_new ( 3, 2, FALSE );
824
825   gtk_table_attach_defaults ( GTK_TABLE(table_utm), ce_label, 0, 1, 0, 1 );
826   gtk_table_attach_defaults ( GTK_TABLE(table_utm), cw.ce_spin, 1, 2, 0, 1 );
827   gtk_table_attach_defaults ( GTK_TABLE(table_utm), cn_label, 0, 1, 1, 2 );
828   gtk_table_attach_defaults ( GTK_TABLE(table_utm), cw.cn_spin, 1, 2, 1, 2 );
829
830   GtkWidget *utm_hbox = gtk_hbox_new ( FALSE, 0 );
831   cw.utm_zone_spin = gtk_spin_button_new ((GtkAdjustment*)gtk_adjustment_new( vgl->corner.zone, 1, 60, 1, 5, 0 ), 1, 0 );
832   gtk_box_pack_start ( GTK_BOX(utm_hbox), gtk_label_new(_("Zone:")), TRUE, TRUE, 0 );
833   gtk_box_pack_start ( GTK_BOX(utm_hbox), cw.utm_zone_spin, TRUE, TRUE, 0 );
834   gtk_box_pack_start ( GTK_BOX(utm_hbox), gtk_label_new(_("Letter:")), TRUE, TRUE, 0 );
835   cw.utm_letter_entry = gtk_entry_new ();
836   gtk_entry_set_max_length ( GTK_ENTRY(cw.utm_letter_entry), 1 );
837   gtk_entry_set_width_chars ( GTK_ENTRY(cw.utm_letter_entry), 2 );
838   gchar tmp_letter[2];
839   tmp_letter[0] = vgl->corner.letter;
840   tmp_letter[1] = '\0';
841   gtk_entry_set_text ( GTK_ENTRY(cw.utm_letter_entry), tmp_letter );
842   gtk_box_pack_start ( GTK_BOX(utm_hbox), cw.utm_letter_entry, TRUE, TRUE, 0 );
843
844   gtk_table_attach_defaults ( GTK_TABLE(table_utm), utm_hbox, 0, 2, 2, 3 );
845
846   // Lat/Lon
847   GtkWidget *table_ll = gtk_table_new ( 5, 2, FALSE );
848
849   GtkWidget *lat_tl_label = gtk_label_new ( _("Upper left latitude:") );
850   cw.lat_tl_spin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new (0.0,-90,90.0,0.05,0.1,0), 0.1, 6 );
851   GtkWidget *lon_tl_label = gtk_label_new ( _("Upper left longitude:") );
852   cw.lon_tl_spin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new (0.0,-180,180.0,0.05,0.1,0), 0.1, 6 );
853   GtkWidget *lat_br_label = gtk_label_new ( _("Lower right latitude:") );
854   cw.lat_br_spin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new (0.0,-90,90.0,0.05,0.1,0), 0.1, 6 );
855   GtkWidget *lon_br_label = gtk_label_new ( _("Lower right longitude:") );
856   cw.lon_br_spin = gtk_spin_button_new ( (GtkAdjustment *) gtk_adjustment_new (0.0,-180.0,180.0,0.05,0.1,0), 0.1, 6 );
857
858   gtk_table_attach_defaults ( GTK_TABLE(table_ll), lat_tl_label, 0, 1, 0, 1 );
859   gtk_table_attach_defaults ( GTK_TABLE(table_ll), cw.lat_tl_spin, 1, 2, 0, 1 );
860   gtk_table_attach_defaults ( GTK_TABLE(table_ll), lon_tl_label, 0, 1, 1, 2 );
861   gtk_table_attach_defaults ( GTK_TABLE(table_ll), cw.lon_tl_spin, 1, 2, 1, 2 );
862   gtk_table_attach_defaults ( GTK_TABLE(table_ll), lat_br_label, 0, 1, 2, 3 );
863   gtk_table_attach_defaults ( GTK_TABLE(table_ll), cw.lat_br_spin, 1, 2, 2, 3 );
864   gtk_table_attach_defaults ( GTK_TABLE(table_ll), lon_br_label, 0, 1, 3, 4 );
865   gtk_table_attach_defaults ( GTK_TABLE(table_ll), cw.lon_br_spin, 1, 2, 3, 4 );
866
867   GtkWidget *calc_mpp_button = gtk_button_new_with_label ( _("Calculate MPP values from coordinates") );
868   gtk_widget_set_tooltip_text ( calc_mpp_button, _("Enter all corner coordinates before calculating the MPP values from the image size") );
869   gtk_table_attach_defaults ( GTK_TABLE(table_ll), calc_mpp_button, 0, 2, 4, 5 );
870
871   VikCoord vc;
872   vik_coord_load_from_utm (&vc, VIK_COORD_LATLON, &(vgl->corner));
873   gtk_spin_button_set_value ( GTK_SPIN_BUTTON(cw.lat_tl_spin), vc.north_south );
874   gtk_spin_button_set_value ( GTK_SPIN_BUTTON(cw.lon_tl_spin), vc.east_west );
875   gtk_spin_button_set_value ( GTK_SPIN_BUTTON(cw.lat_br_spin), vgl->ll_br.lat );
876   gtk_spin_button_set_value ( GTK_SPIN_BUTTON(cw.lon_br_spin), vgl->ll_br.lon );
877
878   gtk_notebook_append_page(GTK_NOTEBOOK(cw.tabs), GTK_WIDGET(table_utm), gtk_label_new(_("UTM")));
879   gtk_notebook_append_page(GTK_NOTEBOOK(cw.tabs), GTK_WIDGET(table_ll), gtk_label_new(_("Latitude/Longitude")));
880   gtk_box_pack_start ( dgbox, cw.tabs, TRUE, TRUE, 0 );
881
882   GtkWidget *alpha_hbox = gtk_hbox_new ( FALSE, 0 );
883   // GTK3 => GtkWidget *alpha_scale = gtk_scale_new_with_range ( GTK_ORIENTATION_HORIZONTAL, 0, 255, 1 );
884   GtkWidget *alpha_scale = gtk_hscale_new_with_range ( 0, 255, 1 );
885   gtk_scale_set_digits ( GTK_SCALE(alpha_scale), 0 );
886   gtk_range_set_value ( GTK_RANGE(alpha_scale), vgl->alpha );
887   gtk_box_pack_start ( GTK_BOX(alpha_hbox), gtk_label_new(_("Alpha:")), TRUE, TRUE, 0 );
888   gtk_box_pack_start ( GTK_BOX(alpha_hbox), alpha_scale, TRUE, TRUE, 0 );
889   gtk_box_pack_start ( dgbox, alpha_hbox, TRUE, TRUE, 0 );
890
891   vgl->cw = cw;
892
893   g_signal_connect ( G_OBJECT(vgl->cw.tabs), "switch-page", G_CALLBACK(switch_tab), vgl );
894   g_signal_connect ( G_OBJECT(calc_mpp_button), "clicked", G_CALLBACK(calculate_mpp_from_coords), vgl );
895
896   g_signal_connect_swapped ( G_OBJECT(wfp_button), "clicked", G_CALLBACK(georef_layer_dialog_load), &cw );
897
898   if ( response_w )
899     gtk_widget_grab_focus ( response_w );
900
901   gtk_widget_show_all ( dialog );
902
903   // Remember setting the notebook page must be done after the widget is visible.
904   gint page_num = 0;
905   if ( a_settings_get_integer ( VIK_SETTINGS_GEOREF_TAB, &page_num ) )
906     if ( page_num < 0 || page_num > 1 )
907       page_num = 0;
908   gtk_notebook_set_current_page ( GTK_NOTEBOOK(cw.tabs), page_num );
909
910   if ( gtk_dialog_run ( GTK_DIALOG(dialog) ) == GTK_RESPONSE_ACCEPT )
911   {
912     align_coords ( vgl );
913
914     vgl->corner.easting = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(cw.ce_spin) );
915     vgl->corner.northing = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(cw.cn_spin) );
916     vgl->corner.zone = gtk_spin_button_get_value_as_int ( GTK_SPIN_BUTTON(cw.utm_zone_spin) );
917     const gchar *letter = gtk_entry_get_text ( GTK_ENTRY(cw.utm_letter_entry) );
918     if (*letter)
919        vgl->corner.letter = toupper(*letter);
920     vgl->mpp_easting = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(cw.x_spin) );
921     vgl->mpp_northing = gtk_spin_button_get_value ( GTK_SPIN_BUTTON(cw.y_spin) );
922     vgl->ll_br = get_ll_br (vgl);
923     check_br_is_good_or_msg_user ( vgl );
924     if ( g_strcmp0 (vgl->image, vik_file_entry_get_filename(VIK_FILE_ENTRY(cw.imageentry)) ) != 0 )
925     {
926       georef_layer_set_image ( vgl, vik_file_entry_get_filename(VIK_FILE_ENTRY(cw.imageentry)) );
927       georef_layer_load_image ( vgl, VIK_VIEWPORT(vp), FALSE );
928     }
929
930     vgl->alpha = (guint8) gtk_range_get_value ( GTK_RANGE(alpha_scale) );
931     if ( vgl->pixbuf && vgl->alpha < 255 )
932       vgl->pixbuf = ui_pixbuf_set_alpha ( vgl->pixbuf, vgl->alpha );
933     if ( vgl->scaled && vgl->alpha < 255 )
934       vgl->scaled = ui_pixbuf_set_alpha ( vgl->scaled, vgl->alpha );
935
936     a_settings_set_integer ( VIK_SETTINGS_GEOREF_TAB, gtk_notebook_get_current_page(GTK_NOTEBOOK(cw.tabs)) );
937
938     gtk_widget_destroy ( GTK_WIDGET(dialog) );
939     return TRUE;
940   }
941   gtk_widget_destroy ( GTK_WIDGET(dialog) );
942   return FALSE;
943 }
944
945 static void georef_layer_zoom_to_fit ( gpointer vgl_vlp[2] )
946 {
947   vik_viewport_set_xmpp ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vgl_vlp[1])), VIK_GEOREF_LAYER(vgl_vlp[0])->mpp_easting );
948   vik_viewport_set_ympp ( vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vgl_vlp[1])), VIK_GEOREF_LAYER(vgl_vlp[0])->mpp_northing );
949   vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vgl_vlp[1]) );
950 }
951
952 static void georef_layer_goto_center ( gpointer vgl_vlp[2] )
953 {
954   VikGeorefLayer *vgl = VIK_GEOREF_LAYER ( vgl_vlp[0] );
955   VikViewport *vp = vik_layers_panel_get_viewport(VIK_LAYERS_PANEL(vgl_vlp[1]));
956   struct UTM utm;
957   VikCoord coord;
958
959   vik_coord_to_utm ( vik_viewport_get_center ( vp ), &utm );
960
961   utm.easting = vgl->corner.easting + (vgl->width * vgl->mpp_easting / 2); /* only an approximation */
962   utm.northing = vgl->corner.northing - (vgl->height * vgl->mpp_northing / 2);
963
964   vik_coord_load_from_utm ( &coord, vik_viewport_get_coord_mode ( vp ), &utm );
965   vik_viewport_set_center_coord ( vp, &coord, TRUE );
966
967   vik_layers_panel_emit_update ( VIK_LAYERS_PANEL(vgl_vlp[1]) );
968 }
969
970 static void georef_layer_add_menu_items ( VikGeorefLayer *vgl, GtkMenu *menu, gpointer vlp )
971 {
972   static gpointer pass_along[2];
973   GtkWidget *item;
974   pass_along[0] = vgl;
975   pass_along[1] = vlp;
976
977   item = gtk_menu_item_new();
978   gtk_menu_shell_append ( GTK_MENU_SHELL(menu), item );
979   gtk_widget_show ( item );
980
981   /* Now with icons */
982   item = gtk_image_menu_item_new_with_mnemonic ( _("_Zoom to Fit Map") );
983   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_ZOOM_FIT, GTK_ICON_SIZE_MENU) );
984   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(georef_layer_zoom_to_fit), pass_along );
985   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
986   gtk_widget_show ( item );
987
988   item = gtk_image_menu_item_new_with_mnemonic ( _("_Goto Map Center") );
989   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_JUMP_TO, GTK_ICON_SIZE_MENU) );
990   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(georef_layer_goto_center), pass_along );
991   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
992   gtk_widget_show ( item );
993
994   item = gtk_image_menu_item_new_with_mnemonic ( _("_Export to World File") );
995   gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_HARDDISK, GTK_ICON_SIZE_MENU) );
996   g_signal_connect_swapped ( G_OBJECT(item), "activate", G_CALLBACK(georef_layer_export_params), pass_along );
997   gtk_menu_shell_append (GTK_MENU_SHELL (menu), item);
998   gtk_widget_show ( item );
999 }
1000
1001
1002 static gpointer georef_layer_move_create ( VikWindow *vw, VikViewport *vvp)
1003 {
1004   return vvp;
1005 }
1006
1007 static gboolean georef_layer_move_release ( VikGeorefLayer *vgl, GdkEventButton *event, VikViewport *vvp )
1008 {
1009   if (!vgl || vgl->vl.type != VIK_LAYER_GEOREF)
1010     return FALSE;
1011
1012   if ( vgl->click_x != -1 )
1013   {
1014     vgl->corner.easting += (event->x - vgl->click_x) * vik_viewport_get_xmpp (vvp);
1015     vgl->corner.northing -= (event->y - vgl->click_y) * vik_viewport_get_ympp (vvp);
1016     vik_layer_emit_update ( VIK_LAYER(vgl) );
1017     return TRUE;
1018   }
1019   return FALSE; /* I didn't move anything on this layer! */
1020 }
1021
1022 static gpointer georef_layer_zoom_create ( VikWindow *vw, VikViewport *vvp)
1023 {
1024   return vvp;
1025 }
1026
1027 static gboolean georef_layer_zoom_press ( VikGeorefLayer *vgl, GdkEventButton *event, VikViewport *vvp )
1028 {
1029   if (!vgl || vgl->vl.type != VIK_LAYER_GEOREF)
1030     return FALSE;
1031   if ( event->button == 1 )
1032   {
1033     if ( vgl->mpp_easting < (VIK_VIEWPORT_MAX_ZOOM / 1.05) && vgl->mpp_northing < (VIK_VIEWPORT_MAX_ZOOM / 1.05) )
1034     {
1035       vgl->mpp_easting *= 1.01;
1036       vgl->mpp_northing *= 1.01;
1037     }
1038   }
1039   else
1040   {
1041     if ( vgl->mpp_easting > (VIK_VIEWPORT_MIN_ZOOM * 1.05) && vgl->mpp_northing > (VIK_VIEWPORT_MIN_ZOOM * 1.05) )
1042     {
1043       vgl->mpp_easting /= 1.01;
1044       vgl->mpp_northing /= 1.01;
1045     }
1046   }
1047   vik_viewport_set_xmpp ( vvp, vgl->mpp_easting );
1048   vik_viewport_set_ympp ( vvp, vgl->mpp_northing );
1049   vik_layer_emit_update ( VIK_LAYER(vgl) );
1050   return TRUE;
1051 }
1052
1053 static gboolean georef_layer_move_press ( VikGeorefLayer *vgl, GdkEventButton *event, VikViewport *vvp )
1054 {
1055   if (!vgl || vgl->vl.type != VIK_LAYER_GEOREF)
1056     return FALSE;
1057   vgl->click_x = event->x;
1058   vgl->click_y = event->y;
1059   return TRUE;
1060 }