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