]> git.street.me.uk Git - andy/viking.git/blob - src/vikdemlayer.c
Some explicit setting of dirpath values to NULL.
[andy/viking.git] / src / vikdemlayer.c
1 /*
2  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3  *
4  * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  *
20  */
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #ifdef HAVE_MATH_H
26 #include <math.h>
27 #endif
28 #ifdef HAVE_SYS_TYPES_H
29 #include <sys/types.h>
30 #endif
31 #ifdef HAVE_SYS_STAT_H
32 #include <sys/stat.h>
33 #endif
34 #ifdef HAVE_STRING_H
35 #include <string.h>
36 #endif
37 #include <stdlib.h>
38 #include <glib/gstdio.h>
39 #include <glib/gi18n.h>
40
41 #include "background.h"
42 #include "viking.h"
43 #include "vikmapslayer.h"
44 #include "vikdemlayer.h"
45 #include "dem.h"
46 #include "dems.h"
47 #include "icons/icons.h"
48
49 #define MAPS_CACHE_DIR maps_layer_default_dir()
50 #define SRTM_CACHE_TEMPLATE "%ssrtm3-%s%s%c%02d%c%03d.hgt.zip"
51
52 #define SRTM_HTTP_BASE_URL "https://dds.cr.usgs.gov/srtm/version2_1/SRTM3"
53 static gchar *base_url = NULL;
54 #define VIK_SETTINGS_SRTM_HTTP_BASE_URL "srtm_http_base_url"
55
56 #ifdef VIK_CONFIG_DEM24K
57 #define DEM24K_DOWNLOAD_SCRIPT "dem24k.pl"
58 #endif
59
60 #define UNUSED_LINE_THICKNESS 3
61
62 static VikDEMLayer *dem_layer_new ( VikViewport *vvp );
63 static void dem_layer_draw ( VikDEMLayer *vdl, VikViewport *vp );
64 static void dem_layer_free ( VikDEMLayer *vdl );
65 static VikDEMLayer *dem_layer_create ( VikViewport *vp );
66 static const gchar* dem_layer_tooltip( VikDEMLayer *vdl );
67 static void dem_layer_marshall( VikDEMLayer *vdl, guint8 **data, gint *len );
68 static VikDEMLayer *dem_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
69 static gboolean dem_layer_set_param ( VikDEMLayer *vdl, VikLayerSetParam *vlsp );
70 static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gboolean is_file_operation );
71 static void dem_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file );
72 static void srtm_draw_existence ( VikViewport *vp );
73
74 #ifdef VIK_CONFIG_DEM24K
75 static void dem24k_draw_existence ( VikViewport *vp );
76 #endif
77
78 /* Upped upper limit incase units are feet */
79 static VikLayerParamScale param_scales[] = {
80   { 0, 30000, 10, 1 },
81   { 1, 30000, 10, 1 },
82 };
83
84 static gchar *params_source[] = {
85         N_("SRTM Global 90m (3 arcsec)"),
86 #ifdef VIK_CONFIG_DEM24K
87         "USA 10m (USGS 24k)",
88 #endif
89         NULL
90         };
91
92 static gchar *params_type[] = {
93         N_("Absolute height"),
94         N_("Height gradient"),
95         NULL
96 };
97
98 enum { DEM_SOURCE_SRTM,
99 #ifdef VIK_CONFIG_DEM24K
100        DEM_SOURCE_DEM24K,
101 #endif
102      };
103
104 enum { DEM_TYPE_HEIGHT = 0,
105        DEM_TYPE_GRADIENT,
106        DEM_TYPE_NONE,
107 };
108
109 static VikLayerParamData color_default ( void ) {
110   VikLayerParamData data; gdk_color_parse ( "blue", &data.c ); return data;
111 }
112
113 static VikLayerParamData source_default ( void ) { return VIK_LPD_UINT ( DEM_SOURCE_SRTM ); }
114 static VikLayerParamData type_default ( void ) { return VIK_LPD_UINT ( DEM_TYPE_HEIGHT ); }
115 static VikLayerParamData min_elev_default ( void ) { return VIK_LPD_DOUBLE ( 0.0 ); }
116 static VikLayerParamData max_elev_default ( void ) { return VIK_LPD_DOUBLE ( 1000.0 ); }
117
118 static VikLayerParam dem_layer_params[] = {
119   { VIK_LAYER_DEM, "files", VIK_LAYER_PARAM_STRING_LIST, VIK_LAYER_GROUP_NONE, N_("DEM Files:"), VIK_LAYER_WIDGET_FILELIST, NULL, NULL, NULL, NULL, NULL, NULL },
120   { VIK_LAYER_DEM, "source", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Download Source:"), VIK_LAYER_WIDGET_RADIOGROUP_STATIC, params_source, NULL, NULL, source_default, NULL, NULL },
121   { VIK_LAYER_DEM, "color", VIK_LAYER_PARAM_COLOR, VIK_LAYER_GROUP_NONE, N_("Min Elev Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, color_default, NULL, NULL },
122   { VIK_LAYER_DEM, "type", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Type:"), VIK_LAYER_WIDGET_RADIOGROUP_STATIC, params_type, NULL, NULL, type_default, NULL, NULL },
123   { VIK_LAYER_DEM, "min_elev", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_GROUP_NONE, N_("Min Elev:"), VIK_LAYER_WIDGET_SPINBUTTON, param_scales + 0, NULL, NULL, min_elev_default, NULL, NULL },
124   { VIK_LAYER_DEM, "max_elev", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_GROUP_NONE, N_("Max Elev:"), VIK_LAYER_WIDGET_SPINBUTTON, param_scales + 0, NULL, NULL, max_elev_default, NULL, NULL },
125 };
126
127
128 enum { PARAM_FILES=0, PARAM_SOURCE, PARAM_COLOR, PARAM_TYPE, PARAM_MIN_ELEV, PARAM_MAX_ELEV, NUM_PARAMS };
129
130 static gpointer dem_layer_download_create ( VikWindow *vw, VikViewport *vvp);
131 static gboolean dem_layer_download_release ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp );
132 static gboolean dem_layer_download_click ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp );
133
134 static VikToolInterface dem_tools[] = {
135   { &demdl_18_pixbuf,
136     { "DEMDownload", "vik-icon-DEM Download", N_("_DEM Download"), NULL, N_("DEM Download"), 0 },
137     (VikToolConstructorFunc) dem_layer_download_create, NULL, NULL, NULL,
138     (VikToolMouseFunc) dem_layer_download_click, NULL,  (VikToolMouseFunc) dem_layer_download_release,
139     (VikToolKeyFunc) NULL,
140     FALSE,
141     GDK_CURSOR_IS_PIXMAP, &cursor_demdl_pixbuf, NULL },
142 };
143
144
145 /* HEIGHT COLORS
146    The first entry is blue for a default 'sea' colour,
147    however the value used by the corresponding gc can be configured as part of the DEM layer properties.
148    The other colours, shaded from brown to white are used to give an indication of height.
149 */
150 static gchar *dem_height_colors[] = {
151 "#0000FF",
152 "#9b793c", "#9c7d40", "#9d8144", "#9e8549", "#9f894d", "#a08d51", "#a29156", "#a3955a", "#a4995e", "#a69d63",
153 "#a89f65", "#aaa267", "#ada569", "#afa76b", "#b1aa6d", "#b4ad6f", "#b6b071", "#b9b373", "#bcb676", "#beb978",
154 "#c0bc7a", "#c2c07d", "#c4c37f", "#c6c681", "#c8ca84", "#cacd86", "#ccd188", "#cfd58b", "#c2ce84", "#b5c87e",
155 "#a9c278", "#9cbb71", "#8fb56b", "#83af65", "#76a95e", "#6aa358", "#5e9d52", "#63a055", "#69a458", "#6fa85c",
156 "#74ac5f", "#7ab063", "#80b467", "#86b86a", "#8cbc6e", "#92c072", "#94c175", "#97c278", "#9ac47c", "#9cc57f",
157 "#9fc682", "#a2c886", "#a4c989", "#a7cb8d", "#aacd91", "#afce99", "#b5d0a1", "#bbd2aa", "#c0d3b2", "#c6d5ba",
158 "#ccd7c3", "#d1d9cb", "#d7dbd4", "#DDDDDD", "#e0e0e0", "#e4e4e4", "#e8e8e8", "#ebebeb", "#efefef", "#f3f3f3",
159 "#f7f7f7", "#fbfbfb", "#ffffff"
160 };
161
162 static const guint DEM_N_HEIGHT_COLORS = sizeof(dem_height_colors)/sizeof(dem_height_colors[0]);
163
164 /*
165 "#9b793c", "#9e8549", "#a29156", "#a69d63", "#ada569", "#b4ad6f", "#bcb676", "#c2c07d", "#c8ca84", "#cfd58b",
166 "#a9c278", "#83af65", "#5e9d52", "#6fa85c", "#80b467", "#92c072", "#9ac47c", "#a2c886", "#aacd91", "#bbd2aa",
167 "#ccd7c3", "#DDDDDD", "#e8e8e8", "#f3f3f3", "#FFFFFF"
168 };
169 */
170
171 static gchar *dem_gradient_colors[] = {
172 "#AAAAAA",
173 "#000000", "#000011", "#000022", "#000033", "#000044", "#00004c", "#000055", "#00005d", "#000066", "#00006e",
174 "#000077", "#00007f", "#000088", "#000090", "#000099", "#0000a1", "#0000aa", "#0000b2", "#0000bb", "#0000c3",
175 "#0000cc", "#0000d4", "#0000dd", "#0000e5", "#0000ee", "#0000f6", "#0000ff", "#0008f7", "#0011ee", "#0019e6",
176 "#0022dd", "#002ad5", "#0033cc", "#003bc4", "#0044bb", "#004cb3", "#0055aa", "#005da2", "#006699", "#006e91",
177 "#007788", "#007f80", "#008877", "#00906f", "#009966", "#00a15e", "#00aa55", "#00b24d", "#00bb44", "#00c33c",
178 "#00cc33", "#00d42b", "#00dd22", "#00e51a", "#00ee11", "#00f609", "#00ff00", "#08f700", "#11ee00", "#19e600",
179 "#22dd00", "#2ad500", "#33cc00", "#3bc400", "#44bb00", "#4cb300", "#55aa00", "#5da200", "#669900", "#6e9100",
180 "#778800", "#7f8000", "#887700", "#906f00", "#996600", "#a15e00", "#aa5500", "#b24d00", "#bb4400", "#c33c00",
181 "#cc3300", "#d42b00", "#dd2200", "#e51a00", "#ee1100", "#f60900", "#ff0000",
182 "#FFFFFF"
183 };
184
185 static const guint DEM_N_GRADIENT_COLORS = sizeof(dem_gradient_colors)/sizeof(dem_gradient_colors[0]);
186
187
188 VikLayerInterface vik_dem_layer_interface = {
189   "DEM",
190   N_("DEM"),
191   "<control><shift>D",
192   &vikdemlayer_pixbuf,
193
194   dem_tools,
195   sizeof(dem_tools) / sizeof(dem_tools[0]),
196
197   dem_layer_params,
198   NUM_PARAMS,
199   NULL,
200   0,
201
202   VIK_MENU_ITEM_ALL,
203
204   (VikLayerFuncCreate)                  dem_layer_create,
205   (VikLayerFuncRealize)                 NULL,
206   (VikLayerFuncPostRead)                dem_layer_post_read,
207   (VikLayerFuncFree)                    dem_layer_free,
208
209   (VikLayerFuncProperties)              NULL,
210   (VikLayerFuncDraw)                    dem_layer_draw,
211   (VikLayerFuncChangeCoordMode)         NULL,
212
213   (VikLayerFuncGetTimestamp)            NULL,
214
215   (VikLayerFuncSetMenuItemsSelection)   NULL,
216   (VikLayerFuncGetMenuItemsSelection)   NULL,
217
218   (VikLayerFuncAddMenuItems)            NULL,
219   (VikLayerFuncSublayerAddMenuItems)    NULL,
220
221   (VikLayerFuncSublayerRenameRequest)   NULL,
222   (VikLayerFuncSublayerToggleVisible)   NULL,
223   (VikLayerFuncSublayerTooltip)         NULL,
224   (VikLayerFuncLayerTooltip)            dem_layer_tooltip,
225   (VikLayerFuncLayerSelected)           NULL,
226
227   (VikLayerFuncMarshall)                dem_layer_marshall,
228   (VikLayerFuncUnmarshall)              dem_layer_unmarshall,
229
230   (VikLayerFuncSetParam)                dem_layer_set_param,
231   (VikLayerFuncGetParam)                dem_layer_get_param,
232   (VikLayerFuncChangeParam)             NULL,
233
234   (VikLayerFuncReadFileData)            NULL,
235   (VikLayerFuncWriteFileData)           NULL,
236
237   (VikLayerFuncDeleteItem)              NULL,
238   (VikLayerFuncCutItem)                 NULL,
239   (VikLayerFuncCopyItem)                NULL,
240   (VikLayerFuncPasteItem)               NULL,
241   (VikLayerFuncFreeCopiedItem)          NULL,
242   (VikLayerFuncDragDropRequest)         NULL,
243
244   (VikLayerFuncSelectClick)             NULL,
245   (VikLayerFuncSelectMove)              NULL,
246   (VikLayerFuncSelectRelease)           NULL,
247   (VikLayerFuncSelectedViewportMenu)    NULL,
248 };
249
250 struct _VikDEMLayer {
251   VikLayer vl;
252   GdkGC **gcs;
253   GdkGC **gcsgradient;
254   GList *files;
255   gdouble min_elev;
256   gdouble max_elev;
257   GdkColor color;
258   guint source;
259   guint type;
260
261   // right click menu only stuff - similar to mapslayer
262   GtkMenu *right_click_menu;
263 };
264
265 // NB Only performed once per program run
266 static void vik_dem_class_init ( VikDEMLayerClass *klass )
267 {
268   // Note if suppling your own base URL - the site must still follow the Continent directory layout
269   if ( ! a_settings_get_string ( VIK_SETTINGS_SRTM_HTTP_BASE_URL, &base_url ) ) {
270     // Otherwise use the default
271     base_url = g_strdup ( SRTM_HTTP_BASE_URL );
272   }
273 }
274
275 GType vik_dem_layer_get_type ()
276 {
277   static GType vdl_type = 0;
278
279   if (!vdl_type)
280   {
281     static const GTypeInfo vdl_info =
282     {
283       sizeof (VikDEMLayerClass),
284       NULL, /* base_init */
285       NULL, /* base_finalize */
286       (GClassInitFunc) vik_dem_class_init, /* class init */
287       NULL, /* class_finalize */
288       NULL, /* class_data */
289       sizeof (VikDEMLayer),
290       0,
291       NULL /* instance init */
292     };
293     vdl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikDEMLayer", &vdl_info, 0 );
294   }
295
296   return vdl_type;
297 }
298
299 static const gchar* dem_layer_tooltip( VikDEMLayer *vdl )
300 {
301   static gchar tmp_buf[100];
302   g_snprintf (tmp_buf, sizeof(tmp_buf), _("Number of files: %d"), g_list_length (vdl->files));
303   return tmp_buf;
304 }
305
306 static void dem_layer_marshall( VikDEMLayer *vdl, guint8 **data, gint *len )
307 {
308   vik_layer_marshall_params ( VIK_LAYER(vdl), data, len );
309 }
310
311 static VikDEMLayer *dem_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
312 {
313   VikDEMLayer *rv = dem_layer_new ( vvp );
314   gint i;
315
316   /* TODO: share GCS between layers */
317   for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ ) {
318     if ( i == 0 )
319       rv->gcs[i] = vik_viewport_new_gc_from_color ( vvp, &(rv->color), UNUSED_LINE_THICKNESS );
320     else
321       rv->gcs[i] = vik_viewport_new_gc ( vvp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
322   }
323   for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
324     rv->gcsgradient[i] = vik_viewport_new_gc ( vvp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
325
326   vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
327   return rv;
328 }
329
330 /* Structure for DEM data used in background thread */
331 typedef struct {
332   VikDEMLayer *vdl;
333 } dem_load_thread_data;
334
335 /*
336  * Function for starting the DEM file loading as a background thread
337  */
338 static int dem_layer_load_list_thread ( dem_load_thread_data *dltd, gpointer threaddata )
339 {
340   int result = 0; // Default to good
341   // Actual Load
342   if ( a_dems_load_list ( &(dltd->vdl->files), threaddata ) ) {
343     // Thread cancelled
344     result = -1;
345   }
346
347   // ATM as each file is processed the screen is not updated (no mechanism exposed to a_dems_load_list)
348   // Thus force draw only at the end, as loading is complete/aborted
349   // Test is helpful to prevent Gtk-CRITICAL warnings if the program is exitted whilst loading
350   if ( IS_VIK_LAYER(dltd->vdl) )
351     vik_layer_emit_update ( VIK_LAYER(dltd->vdl) ); // NB update requested from background thread
352
353   return result;
354 }
355
356 static void dem_layer_thread_data_free ( dem_load_thread_data *data )
357 {
358   // Simple release
359   g_free ( data );
360 }
361
362 static void dem_layer_thread_cancel ( dem_load_thread_data *data )
363 {
364   // Abort loading
365   // Instead of freeing the list, leave it as partially processed
366   // Thus we can see/use what was done
367 }
368
369 /**
370  * Process the list of DEM files and convert each one to a relative path
371  */
372 static GList *dem_layer_convert_to_relative_filenaming ( GList *files )
373 {
374   gchar *cwd = g_get_current_dir();
375   if ( !cwd )
376     return files;
377
378   GList *relfiles = NULL;
379
380   while ( files ) {
381     gchar *file = g_strdup ( file_GetRelativeFilename ( cwd, files->data ) );
382     relfiles = g_list_prepend ( relfiles, file );
383     files = files->next;
384   }
385
386   g_free ( cwd );
387
388   if ( relfiles ) {
389     // Replacing current list, so delete old values first.
390     GList *iter = files;
391     while ( iter ) {
392       g_free ( iter->data );
393       iter = iter->next;
394     }
395     g_list_free ( files );
396
397     return relfiles;
398   }
399
400   return files;
401 }
402
403 gboolean dem_layer_set_param ( VikDEMLayer *vdl, VikLayerSetParam *vlsp )
404 {
405   switch ( vlsp->id )
406   {
407     case PARAM_COLOR: vdl->color = vlsp->data.c; gdk_gc_set_rgb_fg_color ( vdl->gcs[0], &(vdl->color) ); break;
408     case PARAM_SOURCE: vdl->source = vlsp->data.u; break;
409     case PARAM_TYPE: vdl->type = vlsp->data.u; break;
410     case PARAM_MIN_ELEV:
411       /* Convert to store internally
412          NB file operation always in internal units (metres) */
413       if (!vlsp->is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
414         vdl->min_elev = VIK_FEET_TO_METERS(vlsp->data.d);
415       else
416         vdl->min_elev = vlsp->data.d;
417       break;
418     case PARAM_MAX_ELEV:
419       /* Convert to store internally
420          NB file operation always in internal units (metres) */
421       if (!vlsp->is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
422         vdl->max_elev = VIK_FEET_TO_METERS(vlsp->data.d);
423       else
424         vdl->max_elev = vlsp->data.d;
425       break;
426     case PARAM_FILES:
427     {
428       // Clear out old settings - if any commonalities with new settings they will have to be read again
429       a_dems_list_free ( vdl->files );
430
431       // Set file list so any other intermediate screen drawing updates will show currently loaded DEMs by the working thread
432       // Ensure resolving of any relative path names
433       util_make_absolute_filenames ( vdl->files, vlsp->dirpath );
434
435       // No need for thread if no files
436       if ( vdl->files ) {
437         // Thread Load
438         dem_load_thread_data *dltd = g_malloc ( sizeof(dem_load_thread_data) );
439         dltd->vdl = vdl;
440
441         a_background_thread ( BACKGROUND_POOL_LOCAL,
442                               VIK_GTK_WINDOW_FROM_WIDGET(vlsp->vp),
443                               _("DEM Loading"),
444                               (vik_thr_func) dem_layer_load_list_thread,
445                               dltd,
446                               (vik_thr_free_func) dem_layer_thread_data_free,
447                               (vik_thr_free_func) dem_layer_thread_cancel,
448                               g_list_length ( vlsp->data.sl ) ); // Number of DEM files
449       }
450       break;
451     }
452     default: break;
453   }
454   return TRUE;
455 }
456
457 static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gboolean is_file_operation )
458 {
459   VikLayerParamData rv;
460   switch ( id )
461   {
462     case PARAM_FILES:
463       rv.sl = vdl->files;
464       if ( is_file_operation )
465         // Save in relative format if necessary
466         if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE )
467           rv.sl = dem_layer_convert_to_relative_filenaming ( rv.sl );
468       break;
469     case PARAM_SOURCE: rv.u = vdl->source; break;
470     case PARAM_TYPE: rv.u = vdl->type; break;
471     case PARAM_COLOR: rv.c = vdl->color; break;
472     case PARAM_MIN_ELEV:
473       /* Convert for display in desired units
474          NB file operation always in internal units (metres) */
475       if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
476         rv.d = VIK_METERS_TO_FEET(vdl->min_elev);
477       else
478         rv.d = vdl->min_elev;
479       break;
480     case PARAM_MAX_ELEV:
481       /* Convert for display in desired units
482          NB file operation always in internal units (metres) */
483       if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
484         rv.d = VIK_METERS_TO_FEET(vdl->max_elev);
485       else
486         rv.d = vdl->max_elev;
487       break;
488     default: break;
489   }
490   return rv;
491 }
492
493 static void dem_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file )
494 {
495   /* nothing ATM, but keep in case it's needed the future */
496 }
497
498 static VikDEMLayer *dem_layer_new ( VikViewport *vvp )
499 {
500   VikDEMLayer *vdl = VIK_DEM_LAYER ( g_object_new ( VIK_DEM_LAYER_TYPE, NULL ) );
501
502   vik_layer_set_type ( VIK_LAYER(vdl), VIK_LAYER_DEM );
503
504   vdl->files = NULL;
505
506   vdl->gcs = g_malloc(sizeof(GdkGC *)*DEM_N_HEIGHT_COLORS);
507   vdl->gcsgradient = g_malloc(sizeof(GdkGC *)*DEM_N_GRADIENT_COLORS);
508   /* make new gcs only if we need it (copy layer -> use old) */
509
510   // Ensure the base GC is available so the default colour can be applied
511   if ( vvp ) vdl->gcs[0] = vik_viewport_new_gc ( vvp, "#0000FF", 1 );
512
513   vik_layer_set_defaults ( VIK_LAYER(vdl), vvp );
514
515   return vdl;
516 }
517
518
519 static inline guint16 get_height_difference(gint16 elev, gint16 new_elev)
520 {
521   if(new_elev == VIK_DEM_INVALID_ELEVATION)
522     return 0;
523   else
524     return abs(new_elev - elev);
525 }
526
527
528 static void vik_dem_layer_draw_dem ( VikDEMLayer *vdl, VikViewport *vp, VikDEM *dem )
529 {
530   VikDEMColumn *column, *prevcolumn, *nextcolumn;
531
532   struct LatLon dem_northeast, dem_southwest;
533   gdouble max_lat, max_lon, min_lat, min_lon;
534
535   /**** Check if viewport and DEM data overlap ****/
536
537   /* get min, max lat/lon of viewport */
538   vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
539
540   /* get min, max lat/lon of DEM data */
541   if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
542     dem_northeast.lat = dem->max_north / 3600.0;
543     dem_northeast.lon = dem->max_east / 3600.0;
544     dem_southwest.lat = dem->min_north / 3600.0;
545     dem_southwest.lon = dem->min_east / 3600.0;
546   } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
547     struct UTM dem_northeast_utm, dem_southwest_utm;
548     dem_northeast_utm.northing = dem->max_north;
549     dem_northeast_utm.easting = dem->max_east;
550     dem_southwest_utm.northing = dem->min_north;
551     dem_southwest_utm.easting = dem->min_east;
552     dem_northeast_utm.zone = dem_southwest_utm.zone = dem->utm_zone;
553     dem_northeast_utm.letter = dem_southwest_utm.letter = dem->utm_letter;
554
555     a_coords_utm_to_latlon(&dem_northeast_utm, &dem_northeast);
556     a_coords_utm_to_latlon(&dem_southwest_utm, &dem_southwest);
557   } else {
558     // Unknown horiz_units - this shouldn't normally happen
559     // Thus can't work out positions to use
560     return;
561   }
562
563   if ( (max_lat > dem_northeast.lat && min_lat > dem_northeast.lat) ||
564        (max_lat < dem_southwest.lat && min_lat < dem_southwest.lat) )
565     return;
566   else if ( (max_lon > dem_northeast.lon && min_lon > dem_northeast.lon) ||
567             (max_lon < dem_southwest.lon && min_lon < dem_southwest.lon) )
568     return;
569   /* else they overlap */
570
571   /**** End Overlap Check ****/
572   /* boxes to show where we have DEM instead of actually drawing the DEM.
573    * useful if we want to see what areas we have coverage for (if we want
574    * to get elevation data for a track) but don't want to cover the map.
575    */
576
577   #if 0
578   /* draw a box if a DEM is loaded. in future I'd like to add an option for this
579    * this is useful if we want to see what areas we have dem for but don't want to
580    * cover the map (or maybe we just need translucent DEM?) */
581   {
582     VikCoord demne, demsw;
583     gint x1, y1, x2, y2;
584     vik_coord_load_from_latlon(&demne, vik_viewport_get_coord_mode(vp), &dem_northeast);
585     vik_coord_load_from_latlon(&demsw, vik_viewport_get_coord_mode(vp), &dem_southwest);
586
587     vik_viewport_coord_to_screen ( vp, &demne, &x1, &y1 );
588     vik_viewport_coord_to_screen ( vp, &demsw, &x2, &y2 );
589
590     if ( x1 > vik_viewport_get_width(vp) ) x1=vik_viewport_get_width(vp);
591     if ( y2 > vik_viewport_get_height(vp) ) y2=vik_viewport_get_height(vp);
592     if ( x2 < 0 ) x2 = 0;
593     if ( y1 < 0 ) y1 = 0;
594     vik_viewport_draw_rectangle ( vp, gtk_widget_get_style(GTK_WIDGET(vp))->black_gc,
595         FALSE, x2, y1, x1-x2, y2-y1 );
596     return;
597   }
598   #endif
599
600   if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
601     VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
602
603     gdouble max_lat_as, max_lon_as, min_lat_as, min_lon_as;  
604     gdouble start_lat_as, end_lat_as, start_lon_as, end_lon_as;
605
606     gdouble start_lat, end_lat, start_lon, end_lon;
607
608     struct LatLon counter;
609
610     guint x, y, start_x, start_y;
611
612     gint16 elev;
613
614     guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 80 ); /* todo: smarter calculation. */
615
616     gdouble nscale_deg = dem->north_scale / ((gdouble) 3600);
617     gdouble escale_deg = dem->east_scale / ((gdouble) 3600);
618
619     max_lat_as = max_lat * 3600;
620     min_lat_as = min_lat * 3600;
621     max_lon_as = max_lon * 3600;
622     min_lon_as = min_lon * 3600;
623
624     start_lat_as = MAX(min_lat_as, dem->min_north);
625     end_lat_as   = MIN(max_lat_as, dem->max_north);
626     start_lon_as = MAX(min_lon_as, dem->min_east);
627     end_lon_as   = MIN(max_lon_as, dem->max_east);
628
629     start_lat = floor(start_lat_as / dem->north_scale) * nscale_deg;
630     end_lat   = ceil (end_lat_as / dem->north_scale) * nscale_deg;
631     start_lon = floor(start_lon_as / dem->east_scale) * escale_deg;
632     end_lon   = ceil (end_lon_as / dem->east_scale) * escale_deg;
633
634     vik_dem_east_north_to_xy ( dem, start_lon_as, start_lat_as, &start_x, &start_y );
635     guint gradient_skip_factor = 1;
636     if(vdl->type == DEM_TYPE_GRADIENT)
637             gradient_skip_factor = skip_factor;
638
639     /* verify sane elev interval */
640     if ( vdl->max_elev <= vdl->min_elev )
641       vdl->max_elev = vdl->min_elev + 1;
642
643     for ( x=start_x, counter.lon = start_lon; counter.lon <= end_lon+escale_deg*skip_factor; counter.lon += escale_deg * skip_factor, x += skip_factor ) {
644       // NOTE: ( counter.lon <= end_lon + ESCALE_DEG*SKIP_FACTOR ) is neccessary so in high zoom modes,
645       // the leftmost column does also get drawn, if the center point is out of viewport.
646       if ( x < dem->n_columns ) {
647         column = g_ptr_array_index ( dem->columns, x );
648         // get previous and next column. catch out-of-bound.
649         gint32 new_x = x;
650         new_x -= gradient_skip_factor;
651         if(new_x < 1)
652           prevcolumn = g_ptr_array_index ( dem->columns, x+1);
653         else
654           prevcolumn = g_ptr_array_index ( dem->columns, new_x);
655         new_x = x;
656         new_x += gradient_skip_factor;
657         if(new_x >= dem->n_columns)
658           nextcolumn = g_ptr_array_index ( dem->columns, x-1);
659         else
660           nextcolumn = g_ptr_array_index ( dem->columns, new_x);
661
662         for ( y=start_y, counter.lat = start_lat; counter.lat <= end_lat; counter.lat += nscale_deg * skip_factor, y += skip_factor ) {
663           if ( y > column->n_points )
664             break;
665
666           elev = column->points[y];
667
668           // calculate bounding box for drawing
669           gint box_x, box_y, box_width, box_height;
670           struct LatLon box_c;
671           box_c = counter;
672           box_c.lat += (nscale_deg * skip_factor)/2;
673           box_c.lon -= (escale_deg * skip_factor)/2;
674           vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
675           vik_viewport_coord_to_screen(vp, &tmp, &box_x, &box_y);
676           // catch box at borders
677           if(box_x < 0)
678                   box_x = 0;
679           if(box_y < 0)
680                   box_y = 0;
681           box_c.lat -= nscale_deg * skip_factor;
682           box_c.lon += escale_deg * skip_factor;
683           vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
684           vik_viewport_coord_to_screen(vp, &tmp, &box_width, &box_height);
685           box_width -= box_x;
686           box_height -= box_y;
687           // catch box at borders
688           if(box_width < 0 || box_height < 0)
689             // skip this as is out of the viewport (e.g. zoomed in so this point is way off screen)
690             continue;
691
692           gboolean below_minimum = FALSE;
693           if(vdl->type == DEM_TYPE_HEIGHT) {
694             if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev ) {
695               // Prevent 'elev - vdl->min_elev' from being negative so can safely use as array index
696               elev = ceil ( vdl->min_elev );
697               below_minimum = TRUE;
698             }
699             if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
700               elev = vdl->max_elev;
701           }
702
703           {
704             if(vdl->type == DEM_TYPE_GRADIENT) {
705               if( elev == VIK_DEM_INVALID_ELEVATION ) {
706                 /* don't draw it */
707               } else {
708                 // calculate and sum gradient in all directions
709                 gint16 change = 0;
710                 gint32 new_y;
711
712                 // calculate gradient from height points all around the current one
713                 new_y = y - gradient_skip_factor;
714                 if(new_y < 0)
715                         new_y = y;
716                 change += get_height_difference(elev, prevcolumn->points[new_y]);
717                 change += get_height_difference(elev, column->points[new_y]);
718                 change += get_height_difference(elev, nextcolumn->points[new_y]);
719
720                 change += get_height_difference(elev, prevcolumn->points[y]);
721                 change += get_height_difference(elev, nextcolumn->points[y]);
722
723                 new_y = y + gradient_skip_factor;
724                 if(new_y >= column->n_points)
725                         new_y = y;
726                 change += get_height_difference(elev, prevcolumn->points[new_y]);
727                 change += get_height_difference(elev, column->points[new_y]);
728                 change += get_height_difference(elev, nextcolumn->points[new_y]);
729
730                 change = change / ((skip_factor > 1) ? log(skip_factor) : 0.55); // FIXME: better calc.
731
732                 if(change < vdl->min_elev)
733                   // Prevent 'change - vdl->min_elev' from being negative so can safely use as array index
734                   change = ceil ( vdl->min_elev );
735
736                 if(change > vdl->max_elev)
737                   change = vdl->max_elev;
738
739                 // void vik_viewport_draw_rectangle ( VikViewport *vvp, GdkGC *gc, gboolean filled, gint x1, gint y1, gint x2, gint y2 );
740                 vik_viewport_draw_rectangle(vp, vdl->gcsgradient[(gint)floor(((change - vdl->min_elev)/(vdl->max_elev - vdl->min_elev))*(DEM_N_GRADIENT_COLORS-2))+1], TRUE, box_x, box_y, box_width, box_height);
741               }
742             } else {
743               if(vdl->type == DEM_TYPE_HEIGHT) {
744                 if ( elev == VIK_DEM_INVALID_ELEVATION )
745                   ; /* don't draw it */
746                 else if ( elev <= 0 || below_minimum )
747                   /* If 'sea' colour or below the defined mininum draw in the configurable colour */
748                   vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, box_x, box_y, box_width, box_height);
749                 else
750                   vik_viewport_draw_rectangle(vp, vdl->gcs[(gint)floor(((elev - vdl->min_elev)/(vdl->max_elev - vdl->min_elev))*(DEM_N_HEIGHT_COLORS-2))+1], TRUE, box_x, box_y, box_width, box_height);
751               }
752             }
753           }
754         } /* for y= */
755       }
756     } /* for x= */
757   } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
758     gdouble max_nor, max_eas, min_nor, min_eas;
759     gdouble start_nor, start_eas, end_nor, end_eas;
760
761     gint16 elev;
762
763     guint x, y, start_x, start_y;
764
765     VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
766     struct UTM counter;
767
768     guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 10 ); /* todo: smarter calculation. */
769
770     VikCoord tleft, tright, bleft, bright;
771
772     vik_viewport_screen_to_coord ( vp, 0, 0, &tleft );
773     vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), 0, &tright );
774     vik_viewport_screen_to_coord ( vp, 0, vik_viewport_get_height(vp), &bleft );
775     vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), vik_viewport_get_height(vp), &bright );
776
777
778     vik_coord_convert(&tleft, VIK_COORD_UTM);
779     vik_coord_convert(&tright, VIK_COORD_UTM);
780     vik_coord_convert(&bleft, VIK_COORD_UTM);
781     vik_coord_convert(&bright, VIK_COORD_UTM);
782
783     max_nor = MAX(tleft.north_south, tright.north_south);
784     min_nor = MIN(bleft.north_south, bright.north_south);
785     max_eas = MAX(bright.east_west, tright.east_west);
786     min_eas = MIN(bleft.east_west, tleft.east_west);
787
788     start_nor = MAX(min_nor, dem->min_north);
789     end_nor   = MIN(max_nor, dem->max_north);
790     if ( tleft.utm_zone == dem->utm_zone && bleft.utm_zone == dem->utm_zone
791          && (tleft.utm_letter >= 'N') == (dem->utm_letter >= 'N')
792          && (bleft.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
793       start_eas = MAX(min_eas, dem->min_east);
794     else
795       start_eas = dem->min_east;
796     if ( tright.utm_zone == dem->utm_zone && bright.utm_zone == dem->utm_zone
797          && (tright.utm_letter >= 'N') == (dem->utm_letter >= 'N')
798          && (bright.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
799       end_eas = MIN(max_eas, dem->max_east);
800     else
801       end_eas = dem->max_east;
802
803     start_nor = floor(start_nor / dem->north_scale) * dem->north_scale;
804     end_nor   = ceil (end_nor / dem->north_scale) * dem->north_scale;
805     start_eas = floor(start_eas / dem->east_scale) * dem->east_scale;
806     end_eas   = ceil (end_eas / dem->east_scale) * dem->east_scale;
807
808     vik_dem_east_north_to_xy ( dem, start_eas, start_nor, &start_x, &start_y );
809
810     /* TODO: why start_x and start_y are -1 -- rounding error from above? */
811
812     counter.zone = dem->utm_zone;
813     counter.letter = dem->utm_letter;
814
815     for ( x=start_x, counter.easting = start_eas; counter.easting <= end_eas; counter.easting += dem->east_scale * skip_factor, x += skip_factor ) {
816       if ( x > 0 && x < dem->n_columns ) {
817         column = g_ptr_array_index ( dem->columns, x );
818         for ( y=start_y, counter.northing = start_nor; counter.northing <= end_nor; counter.northing += dem->north_scale * skip_factor, y += skip_factor ) {
819           if ( y > column->n_points )
820             continue;
821           elev = column->points[y];
822           if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev )
823             elev=vdl->min_elev;
824           if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
825             elev=vdl->max_elev;
826
827           {
828             gint a, b;
829             vik_coord_load_from_utm(&tmp, vik_viewport_get_coord_mode(vp), &counter);
830                     vik_viewport_coord_to_screen(vp, &tmp, &a, &b);
831             if ( elev == VIK_DEM_INVALID_ELEVATION )
832               ; /* don't draw it */
833             else if ( elev <= 0 )
834               vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, a-1, b-1, 2, 2 );
835             else
836               vik_viewport_draw_rectangle(vp, vdl->gcs[(gint)floor((elev - vdl->min_elev)/(vdl->max_elev - vdl->min_elev)*(DEM_N_HEIGHT_COLORS-2))+1], TRUE, a-1, b-1, 2, 2 );
837           }
838         } /* for y= */
839       }
840     } /* for x= */
841   }
842 }
843
844 /* return the continent for the specified lat, lon */
845 /* TODO */
846 static const gchar *srtm_continent_dir ( gint lat, gint lon )
847 {
848   extern const char *_srtm_continent_data[];
849   static GHashTable *srtm_continent = NULL;
850   const gchar *continent;
851   gchar name[16];
852
853   if (!srtm_continent) {
854     const gchar **s;
855
856     srtm_continent = g_hash_table_new(g_str_hash, g_str_equal);
857     s = _srtm_continent_data;
858     while (*s != (gchar *)-1) {
859       continent = *s++;
860       while (*s) {
861         g_hash_table_insert(srtm_continent, (gpointer) *s, (gpointer) continent);
862         s++;
863       }
864       s++;
865     }
866   }
867   g_snprintf(name, sizeof(name), "%c%02d%c%03d",
868                   (lat >= 0) ? 'N' : 'S', ABS(lat),
869                   (lon >= 0) ? 'E' : 'W', ABS(lon));
870
871   return(g_hash_table_lookup(srtm_continent, name));
872 }
873
874 static void dem_layer_draw ( VikDEMLayer *vdl, VikViewport *vp )
875 {
876   GList *dems_iter = vdl->files;
877   VikDEM *dem;
878
879
880   /* search for SRTM3 90m */
881
882   if ( vdl->source == DEM_SOURCE_SRTM )
883     srtm_draw_existence ( vp );
884 #ifdef VIK_CONFIG_DEM24K
885   else if ( vdl->source == DEM_SOURCE_DEM24K )
886     dem24k_draw_existence ( vp );
887 #endif
888
889   while ( dems_iter ) {
890     dem = a_dems_get ( (const char *) (dems_iter->data) );
891     if ( dem )
892       vik_dem_layer_draw_dem ( vdl, vp, dem );
893     dems_iter = dems_iter->next;
894   }
895 }
896
897 static void dem_layer_free ( VikDEMLayer *vdl )
898 {
899   gint i;
900   if ( vdl->gcs )
901     for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
902       g_object_unref ( vdl->gcs[i] );
903   g_free ( vdl->gcs );
904
905   if ( vdl->gcsgradient )
906     for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
907       g_object_unref ( vdl->gcsgradient[i] );
908   g_free ( vdl->gcsgradient );
909
910   a_dems_list_free ( vdl->files );
911 }
912
913 VikDEMLayer *dem_layer_create ( VikViewport *vp )
914 {
915   VikDEMLayer *vdl = dem_layer_new ( vp );
916   gint i;
917   if ( vp ) {
918     /* TODO: share GCS between layers */
919     for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ ) {
920       if ( i > 0 )
921         vdl->gcs[i] = vik_viewport_new_gc ( vp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
922     }
923     for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
924       vdl->gcsgradient[i] = vik_viewport_new_gc ( vp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
925   }
926   return vdl;
927 }
928 /**************************************************************
929  **** SOURCES & DOWNLOADING
930  **************************************************************/
931 typedef struct {
932   gchar *dest;
933   gdouble lat, lon;
934
935   GMutex *mutex;
936   VikDEMLayer *vdl; /* NULL if not alive */
937
938   guint source;
939 } DEMDownloadParams;
940
941
942 /**************************************************
943  *  SOURCE: SRTM                                  *
944  **************************************************/
945
946 static void srtm_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
947 {
948   gint intlat, intlon;
949   const gchar *continent_dir;
950
951   intlat = (int)floor(p->lat);
952   intlon = (int)floor(p->lon);
953   continent_dir = srtm_continent_dir(intlat, intlon);
954
955   if (!continent_dir) {
956     if ( p->vdl ) {
957       gchar *msg = g_strdup_printf ( _("No SRTM data available for %f, %f"), p->lat, p->lon );
958       vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(p->vdl), msg, VIK_STATUSBAR_INFO );
959       g_free ( msg );
960     }
961     return;
962   }
963
964   gchar *src_url = g_strdup_printf("%s/%s/%c%02d%c%03d.hgt.zip",
965                 base_url,
966                 continent_dir,
967                 (intlat >= 0) ? 'N' : 'S',
968                 ABS(intlat),
969                 (intlon >= 0) ? 'E' : 'W',
970                 ABS(intlon) );
971
972   static DownloadFileOptions options = { FALSE, FALSE, NULL, 5, a_check_map_file, NULL, NULL };
973   DownloadResult_t result = a_http_download_get_url ( src_url, NULL, p->dest, &options, NULL );
974   switch ( result ) {
975     case DOWNLOAD_PARAMETERS_ERROR:
976     case DOWNLOAD_CONTENT_ERROR:
977     case DOWNLOAD_HTTP_ERROR: {
978       gchar *msg = g_strdup_printf ( _("DEM download failure for %f, %f"), p->lat, p->lon );
979       vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(p->vdl), msg, VIK_STATUSBAR_INFO );
980       g_free ( msg );
981       break;
982     }
983     case DOWNLOAD_FILE_WRITE_ERROR: {
984       gchar *msg = g_strdup_printf ( _("DEM write failure for %s"), p->dest );
985       vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(p->vdl), msg, VIK_STATUSBAR_INFO );
986       g_free ( msg );
987       break;
988     }
989     case DOWNLOAD_SUCCESS:
990     case DOWNLOAD_NOT_REQUIRED:
991     default:
992       break;
993   }
994   g_free ( src_url );
995 }
996
997 static gchar *srtm_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
998 {
999   gint intlat, intlon;
1000   const gchar *continent_dir;
1001
1002   intlat = (int)floor(lat);
1003   intlon = (int)floor(lon);
1004   continent_dir = srtm_continent_dir(intlat, intlon);
1005
1006   if (!continent_dir)
1007     continent_dir = "nowhere";
1008
1009   return g_strdup_printf("srtm3-%s%s%c%02d%c%03d.hgt.zip",
1010                 continent_dir,
1011                 G_DIR_SEPARATOR_S,
1012                 (intlat >= 0) ? 'N' : 'S',
1013                 ABS(intlat),
1014                 (intlon >= 0) ? 'E' : 'W',
1015                 ABS(intlon) );
1016
1017 }
1018
1019 /* TODO: generalize */
1020 static void srtm_draw_existence ( VikViewport *vp )
1021 {
1022   gdouble max_lat, max_lon, min_lat, min_lon;  
1023   gchar buf[strlen(MAPS_CACHE_DIR)+strlen(SRTM_CACHE_TEMPLATE)+30];
1024   gint i, j;
1025
1026   vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
1027
1028   for (i = floor(min_lat); i <= floor(max_lat); i++) {
1029     for (j = floor(min_lon); j <= floor(max_lon); j++) {
1030       const gchar *continent_dir;
1031       if ((continent_dir = srtm_continent_dir(i, j)) == NULL)
1032         continue;
1033       g_snprintf(buf, sizeof(buf), SRTM_CACHE_TEMPLATE,
1034                 MAPS_CACHE_DIR,
1035                 continent_dir,
1036                 G_DIR_SEPARATOR_S,
1037                 (i >= 0) ? 'N' : 'S',
1038                 ABS(i),
1039                 (j >= 0) ? 'E' : 'W',
1040                 ABS(j) );
1041       if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1042         VikCoord ne, sw;
1043         gint x1, y1, x2, y2;
1044         sw.north_south = i;
1045         sw.east_west = j;
1046         sw.mode = VIK_COORD_LATLON;
1047         ne.north_south = i+1;
1048         ne.east_west = j+1;
1049         ne.mode = VIK_COORD_LATLON;
1050         vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
1051         vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
1052         if ( x1 < 0 ) x1 = 0;
1053         if ( y2 < 0 ) y2 = 0;
1054         vik_viewport_draw_rectangle ( vp, gtk_widget_get_style(GTK_WIDGET(vp))->black_gc,
1055                 FALSE, x1, y2, x2-x1, y1-y2 );
1056       }
1057     }
1058   }
1059 }
1060
1061
1062 /**************************************************
1063  *  SOURCE: USGS 24K                              *
1064  **************************************************/
1065
1066 #ifdef VIK_CONFIG_DEM24K
1067
1068 static void dem24k_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
1069 {
1070   /* TODO: dest dir */
1071   gchar *cmdline = g_strdup_printf("%s %.03f %.03f",
1072         DEM24K_DOWNLOAD_SCRIPT,
1073         floor(p->lat*8)/8,
1074         ceil(p->lon*8)/8 );
1075   /* FIX: don't use system, use execv or something. check for existence */
1076   system(cmdline);
1077   g_free ( cmdline );
1078 }
1079
1080 static gchar *dem24k_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
1081 {
1082   return g_strdup_printf("dem24k/%d/%d/%.03f,%.03f.dem",
1083         (gint) lat,
1084         (gint) lon,
1085         floor(lat*8)/8,
1086         ceil(lon*8)/8);
1087 }
1088
1089 /* TODO: generalize */
1090 static void dem24k_draw_existence ( VikViewport *vp )
1091 {
1092   gdouble max_lat, max_lon, min_lat, min_lon;  
1093   gchar buf[strlen(MAPS_CACHE_DIR)+40];
1094   gdouble i, j;
1095
1096   vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
1097
1098   for (i = floor(min_lat*8)/8; i <= floor(max_lat*8)/8; i+=0.125) {
1099     /* check lat dir first -- faster */
1100     g_snprintf(buf, sizeof(buf), "%sdem24k/%d/",
1101         MAPS_CACHE_DIR,
1102         (gint) i );
1103     if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1104       continue;
1105     for (j = floor(min_lon*8)/8; j <= floor(max_lon*8)/8; j+=0.125) {
1106       /* check lon dir first -- faster */
1107       g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/",
1108         MAPS_CACHE_DIR,
1109         (gint) i,
1110         (gint) j );
1111       if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1112         continue;
1113       g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/%.03f,%.03f.dem",
1114                 MAPS_CACHE_DIR,
1115                 (gint) i,
1116                 (gint) j,
1117                 floor(i*8)/8,
1118                 floor(j*8)/8 );
1119       if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1120         VikCoord ne, sw;
1121         gint x1, y1, x2, y2;
1122         sw.north_south = i;
1123         sw.east_west = j-0.125;
1124         sw.mode = VIK_COORD_LATLON;
1125         ne.north_south = i+0.125;
1126         ne.east_west = j;
1127         ne.mode = VIK_COORD_LATLON;
1128         vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
1129         vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
1130         if ( x1 < 0 ) x1 = 0;
1131         if ( y2 < 0 ) y2 = 0;
1132         vik_viewport_draw_rectangle ( vp, gtk_widget_get_style(GTK_WIDGET(vp))->black_gc,
1133                 FALSE, x1, y2, x2-x1, y1-y2 );
1134       }
1135     }
1136   }
1137 }
1138 #endif
1139
1140 /**************************************************
1141  *   SOURCES -- DOWNLOADING & IMPORTING TOOL      *
1142  **************************************************
1143  */
1144
1145 static void weak_ref_cb ( gpointer ptr, GObject * dead_vdl )
1146 {
1147   DEMDownloadParams *p = ptr;
1148   g_mutex_lock ( p->mutex );
1149   p->vdl = NULL;
1150   g_mutex_unlock ( p->mutex );
1151 }
1152
1153 /* Try to add file full_path.
1154  * filename will be copied.
1155  * returns FALSE if file does not exists, TRUE otherwise.
1156  */
1157 static gboolean dem_layer_add_file ( VikDEMLayer *vdl, const gchar *filename )
1158 {
1159   if ( g_file_test(filename, G_FILE_TEST_EXISTS) == TRUE ) {
1160     /* only load if file size is not 0 (not in progress) */
1161     GStatBuf sb;
1162     (void)g_stat ( filename, &sb );
1163     if ( sb.st_size ) {
1164       gchar *duped_path = g_strdup(filename);
1165       vdl->files = g_list_prepend ( vdl->files, duped_path );
1166       a_dems_load ( duped_path );
1167       g_debug("%s: %s", __FUNCTION__, duped_path);
1168     }
1169     return TRUE;
1170   } else
1171     return FALSE;
1172 }
1173
1174 static void dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
1175 {
1176   if ( p->source == DEM_SOURCE_SRTM )
1177     srtm_dem_download_thread ( p, threaddata );
1178 #ifdef VIK_CONFIG_DEM24K
1179   else if ( p->source == DEM_SOURCE_DEM24K )
1180     dem24k_dem_download_thread ( p, threaddata );
1181 #endif
1182   else
1183     return;
1184
1185   g_mutex_lock ( p->mutex );
1186   if ( p->vdl ) {
1187     g_object_weak_unref ( G_OBJECT(p->vdl), weak_ref_cb, p );
1188
1189     if ( dem_layer_add_file ( p->vdl, p->dest ) )
1190       vik_layer_emit_update ( VIK_LAYER(p->vdl) ); // NB update requested from background thread
1191   }
1192   g_mutex_unlock ( p->mutex );
1193 }
1194
1195
1196 static void free_dem_download_params ( DEMDownloadParams *p )
1197 {
1198   vik_mutex_free ( p->mutex );
1199   g_free ( p->dest );
1200   g_free ( p );
1201 }
1202
1203 static gpointer dem_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1204 {
1205   return vvp;
1206 }
1207
1208 /**
1209  * Display a simple dialog with information about the DEM file at this location
1210  */
1211 static void dem_layer_file_info ( GtkWidget *widget, struct LatLon *ll )
1212 {
1213   gint intlat, intlon;
1214   const gchar *continent_dir;
1215
1216   intlat = (int)floor(ll->lat);
1217   intlon = (int)floor(ll->lon);
1218   continent_dir = srtm_continent_dir(intlat, intlon);
1219
1220   gchar *source = NULL;
1221   if ( continent_dir )
1222     source = g_strdup_printf ( "%s/%s/%c%02d%c%03d.hgt.zip",
1223                                base_url,
1224                                continent_dir,
1225                                (intlat >= 0) ? 'N' : 'S',
1226                                ABS(intlat),
1227                                (intlon >= 0) ? 'E' : 'W',
1228                                ABS(intlon) );
1229   else
1230     // Probably not over any land...
1231     source = g_strdup ( _("No DEM File Available") );
1232
1233   gchar *filename = NULL;
1234   gchar *dem_file = NULL;
1235 #ifdef VIK_CONFIG_DEM24K
1236   dem_file = dem24k_lat_lon_to_dest_fn ( ll->lat, ll->lon );
1237 #else
1238   dem_file = srtm_lat_lon_to_dest_fn ( ll->lat, ll->lon );
1239 #endif
1240   gchar *message = NULL;
1241
1242   filename = g_strdup_printf ( "%s%s", MAPS_CACHE_DIR, dem_file );
1243
1244   if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1245     // Get some timestamp information of the file
1246     GStatBuf stat_buf;
1247     if ( g_stat ( filename, &stat_buf ) == 0 ) {
1248       gchar time_buf[64];
1249       strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
1250       message = g_strdup_printf ( _("\nSource: %s\n\nDEM File: %s\nDEM File Timestamp: %s"), source, filename, time_buf );
1251     }
1252   }
1253   else
1254     message = g_strdup_printf ( _("Source: %s\n\nNo DEM File!"), source );
1255
1256   // Show the info
1257   a_dialog_info_msg ( GTK_WINDOW(gtk_widget_get_toplevel(widget)), message );
1258
1259   g_free ( message );
1260   g_free ( source );
1261   g_free ( dem_file );
1262   g_free ( filename );
1263 }
1264
1265 static gboolean dem_layer_download_release ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1266 {
1267   VikCoord coord;
1268   static struct LatLon ll;
1269
1270   gchar *full_path;
1271   gchar *dem_file = NULL;
1272
1273   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1274   vik_coord_to_latlon ( &coord, &ll );
1275
1276   
1277   if ( vdl->source == DEM_SOURCE_SRTM )
1278     dem_file = srtm_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1279 #ifdef VIK_CONFIG_DEM24K
1280   else if ( vdl->source == DEM_SOURCE_DEM24K )
1281     dem_file = dem24k_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1282 #endif
1283
1284   if ( ! dem_file )
1285     return TRUE;
1286
1287   full_path = g_strdup_printf("%s%s", MAPS_CACHE_DIR, dem_file );
1288
1289   g_debug("%s: %s", __FUNCTION__, full_path);
1290
1291   if ( event->button == 1 ) {
1292     // TODO: check if already in filelist
1293     if ( ! dem_layer_add_file(vdl, full_path) ) {
1294       gchar *tmp = g_strdup_printf ( _("Downloading DEM %s"), dem_file );
1295       DEMDownloadParams *p = g_malloc(sizeof(DEMDownloadParams));
1296       p->dest = g_strdup(full_path);
1297       p->lat = ll.lat;
1298       p->lon = ll.lon;
1299       p->vdl = vdl;
1300       p->mutex = vik_mutex_new();
1301       p->source = vdl->source;
1302       g_object_weak_ref(G_OBJECT(p->vdl), weak_ref_cb, p );
1303
1304       a_background_thread ( BACKGROUND_POOL_REMOTE,
1305                             VIK_GTK_WINDOW_FROM_LAYER(vdl), tmp,
1306                             (vik_thr_func) dem_download_thread, p,
1307                             (vik_thr_free_func) free_dem_download_params, NULL, 1 );
1308
1309       g_free ( tmp );
1310     }
1311     else
1312       vik_layer_emit_update ( VIK_LAYER(vdl) );
1313   }
1314   else {
1315     if ( !vdl->right_click_menu ) {
1316       GtkWidget *item;
1317       vdl->right_click_menu = GTK_MENU ( gtk_menu_new () );
1318
1319       item = gtk_image_menu_item_new_with_mnemonic ( _("_Show DEM File Information") );
1320       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
1321       g_signal_connect ( G_OBJECT(item), "activate", G_CALLBACK(dem_layer_file_info), &ll );
1322       gtk_menu_shell_append (GTK_MENU_SHELL(vdl->right_click_menu), item);
1323     }
1324
1325     gtk_menu_popup ( vdl->right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1326     gtk_widget_show_all ( GTK_WIDGET(vdl->right_click_menu) );
1327   }
1328
1329   g_free ( dem_file );
1330   g_free ( full_path );
1331
1332   return TRUE;
1333 }
1334
1335 static gboolean dem_layer_download_click ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1336 {
1337 /* choose & keep track of cache dir
1338  * download in background thread
1339  * download over area */
1340   return TRUE;
1341 }
1342
1343