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