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