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