]> git.street.me.uk Git - andy/viking.git/blob - src/vikdemlayer.c
Add option to auto connect to GPSD rather than having to manually control
[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   //gdk_threads_enter();
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 from background thread
352   //gdk_threads_leave();
353
354   return result;
355 }
356
357 static void dem_layer_thread_data_free ( dem_load_thread_data *data )
358 {
359   // Simple release
360   g_free ( data );
361 }
362
363 static void dem_layer_thread_cancel ( dem_load_thread_data *data )
364 {
365   // Abort loading
366   // Instead of freeing the list, leave it as partially processed
367   // Thus we can see/use what was done
368 }
369
370 /**
371  * Process the list of DEM files and convert each one to a relative path
372  */
373 static GList *dem_layer_convert_to_relative_filenaming ( GList *files )
374 {
375   gchar *cwd = g_get_current_dir();
376   if ( !cwd )
377     return files;
378
379   GList *relfiles = NULL;
380
381   while ( files ) {
382     gchar *file = g_strdup ( file_GetRelativeFilename ( cwd, files->data ) );
383     relfiles = g_list_prepend ( relfiles, file );
384     files = files->next;
385   }
386
387   g_free ( cwd );
388
389   if ( relfiles ) {
390     // Replacing current list, so delete old values first.
391     GList *iter = files;
392     while ( iter ) {
393       g_free ( iter->data );
394       iter = iter->next;
395     }
396     g_list_free ( files );
397
398     return relfiles;
399   }
400
401   return files;
402 }
403
404 gboolean dem_layer_set_param ( VikDEMLayer *vdl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
405 {
406   switch ( id )
407   {
408     case PARAM_COLOR: vdl->color = data.c; gdk_gc_set_rgb_fg_color ( vdl->gcs[0], &(vdl->color) ); break;
409     case PARAM_SOURCE: vdl->source = data.u; break;
410     case PARAM_TYPE: vdl->type = data.u; break;
411     case PARAM_MIN_ELEV:
412       /* Convert to store internally
413          NB file operation always in internal units (metres) */
414       if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
415         vdl->min_elev = VIK_FEET_TO_METERS(data.d);
416       else
417         vdl->min_elev = data.d;
418       break;
419     case PARAM_MAX_ELEV:
420       /* Convert to store internally
421          NB file operation always in internal units (metres) */
422       if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
423         vdl->max_elev = VIK_FEET_TO_METERS(data.d);
424       else
425         vdl->max_elev = data.d;
426       break;
427     case PARAM_FILES:
428     {
429       // Clear out old settings - if any commonalities with new settings they will have to be read again
430       a_dems_list_free ( vdl->files );
431       // Set file list so any other intermediate screen drawing updates will show currently loaded DEMs by the working thread
432       vdl->files = data.sl;
433       // No need for thread if no files
434       if ( vdl->files ) {
435         // Thread Load
436         dem_load_thread_data *dltd = g_malloc ( sizeof(dem_load_thread_data) );
437         dltd->vdl = vdl;
438         dltd->vdl->files = data.sl;
439
440         a_background_thread ( BACKGROUND_POOL_LOCAL,
441                               VIK_GTK_WINDOW_FROM_WIDGET(vp),
442                               _("DEM Loading"),
443                               (vik_thr_func) dem_layer_load_list_thread,
444                               dltd,
445                               (vik_thr_free_func) dem_layer_thread_data_free,
446                               (vik_thr_free_func) dem_layer_thread_cancel,
447                               g_list_length ( data.sl ) ); // Number of DEM files
448       }
449       break;
450     }
451     default: break;
452   }
453   return TRUE;
454 }
455
456 static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gboolean is_file_operation )
457 {
458   VikLayerParamData rv;
459   switch ( id )
460   {
461     case PARAM_FILES:
462       rv.sl = vdl->files;
463       if ( is_file_operation )
464         // Save in relative format if necessary
465         if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE )
466           rv.sl = dem_layer_convert_to_relative_filenaming ( rv.sl );
467       break;
468     case PARAM_SOURCE: rv.u = vdl->source; break;
469     case PARAM_TYPE: rv.u = vdl->type; break;
470     case PARAM_COLOR: rv.c = vdl->color; break;
471     case PARAM_MIN_ELEV:
472       /* Convert for display in desired units
473          NB file operation always in internal units (metres) */
474       if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
475         rv.d = VIK_METERS_TO_FEET(vdl->min_elev);
476       else
477         rv.d = vdl->min_elev;
478       break;
479     case PARAM_MAX_ELEV:
480       /* Convert for display in desired units
481          NB file operation always in internal units (metres) */
482       if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
483         rv.d = VIK_METERS_TO_FEET(vdl->max_elev);
484       else
485         rv.d = vdl->max_elev;
486       break;
487     default: break;
488   }
489   return rv;
490 }
491
492 static void dem_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file )
493 {
494   /* nothing ATM, but keep in case it's needed the future */
495 }
496
497 static VikDEMLayer *dem_layer_new ( VikViewport *vvp )
498 {
499   VikDEMLayer *vdl = VIK_DEM_LAYER ( g_object_new ( VIK_DEM_LAYER_TYPE, NULL ) );
500
501   vik_layer_set_type ( VIK_LAYER(vdl), VIK_LAYER_DEM );
502
503   vdl->files = NULL;
504
505   vdl->gcs = g_malloc(sizeof(GdkGC *)*DEM_N_HEIGHT_COLORS);
506   vdl->gcsgradient = g_malloc(sizeof(GdkGC *)*DEM_N_GRADIENT_COLORS);
507   /* make new gcs only if we need it (copy layer -> use old) */
508
509   // Ensure the base GC is available so the default colour can be applied
510   if ( vvp ) vdl->gcs[0] = vik_viewport_new_gc ( vvp, "#0000FF", 1 );
511
512   vik_layer_set_defaults ( VIK_LAYER(vdl), vvp );
513
514   return vdl;
515 }
516
517
518 static inline guint16 get_height_difference(gint16 elev, gint16 new_elev)
519 {
520   if(new_elev == VIK_DEM_INVALID_ELEVATION)
521     return 0;
522   else
523     return abs(new_elev - elev);
524 }
525
526
527 static void vik_dem_layer_draw_dem ( VikDEMLayer *vdl, VikViewport *vp, VikDEM *dem )
528 {
529   VikDEMColumn *column, *prevcolumn, *nextcolumn;
530
531   struct LatLon dem_northeast, dem_southwest;
532   gdouble max_lat, max_lon, min_lat, min_lon;
533
534   /**** Check if viewport and DEM data overlap ****/
535
536   /* get min, max lat/lon of viewport */
537   vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
538
539   /* get min, max lat/lon of DEM data */
540   if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
541     dem_northeast.lat = dem->max_north / 3600.0;
542     dem_northeast.lon = dem->max_east / 3600.0;
543     dem_southwest.lat = dem->min_north / 3600.0;
544     dem_southwest.lon = dem->min_east / 3600.0;
545   } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
546     struct UTM dem_northeast_utm, dem_southwest_utm;
547     dem_northeast_utm.northing = dem->max_north;
548     dem_northeast_utm.easting = dem->max_east;
549     dem_southwest_utm.northing = dem->min_north;
550     dem_southwest_utm.easting = dem->min_east;
551     dem_northeast_utm.zone = dem_southwest_utm.zone = dem->utm_zone;
552     dem_northeast_utm.letter = dem_southwest_utm.letter = dem->utm_letter;
553
554     a_coords_utm_to_latlon(&dem_northeast_utm, &dem_northeast);
555     a_coords_utm_to_latlon(&dem_southwest_utm, &dem_southwest);
556   } else {
557     // Unknown horiz_units - this shouldn't normally happen
558     // Thus can't work out positions to use
559     return;
560   }
561
562   if ( (max_lat > dem_northeast.lat && min_lat > dem_northeast.lat) ||
563        (max_lat < dem_southwest.lat && min_lat < dem_southwest.lat) )
564     return;
565   else if ( (max_lon > dem_northeast.lon && min_lon > dem_northeast.lon) ||
566             (max_lon < dem_southwest.lon && min_lon < dem_southwest.lon) )
567     return;
568   /* else they overlap */
569
570   /**** End Overlap Check ****/
571   /* boxes to show where we have DEM instead of actually drawing the DEM.
572    * useful if we want to see what areas we have coverage for (if we want
573    * to get elevation data for a track) but don't want to cover the map.
574    */
575
576   #if 0
577   /* draw a box if a DEM is loaded. in future I'd like to add an option for this
578    * this is useful if we want to see what areas we have dem for but don't want to
579    * cover the map (or maybe we just need translucent DEM?) */
580   {
581     VikCoord demne, demsw;
582     gint x1, y1, x2, y2;
583     vik_coord_load_from_latlon(&demne, vik_viewport_get_coord_mode(vp), &dem_northeast);
584     vik_coord_load_from_latlon(&demsw, vik_viewport_get_coord_mode(vp), &dem_southwest);
585
586     vik_viewport_coord_to_screen ( vp, &demne, &x1, &y1 );
587     vik_viewport_coord_to_screen ( vp, &demsw, &x2, &y2 );
588
589     if ( x1 > vik_viewport_get_width(vp) ) x1=vik_viewport_get_width(vp);
590     if ( y2 > vik_viewport_get_height(vp) ) y2=vik_viewport_get_height(vp);
591     if ( x2 < 0 ) x2 = 0;
592     if ( y1 < 0 ) y1 = 0;
593     vik_viewport_draw_rectangle ( vp, gtk_widget_get_style(GTK_WIDGET(vp))->black_gc,
594         FALSE, x2, y1, x1-x2, y2-y1 );
595     return;
596   }
597   #endif
598
599   if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
600     VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
601
602     gdouble max_lat_as, max_lon_as, min_lat_as, min_lon_as;  
603     gdouble start_lat_as, end_lat_as, start_lon_as, end_lon_as;
604
605     gdouble start_lat, end_lat, start_lon, end_lon;
606
607     struct LatLon counter;
608
609     guint x, y, start_x, start_y;
610
611     gint16 elev;
612
613     guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 80 ); /* todo: smarter calculation. */
614
615     gdouble nscale_deg = dem->north_scale / ((gdouble) 3600);
616     gdouble escale_deg = dem->east_scale / ((gdouble) 3600);
617
618     max_lat_as = max_lat * 3600;
619     min_lat_as = min_lat * 3600;
620     max_lon_as = max_lon * 3600;
621     min_lon_as = min_lon * 3600;
622
623     start_lat_as = MAX(min_lat_as, dem->min_north);
624     end_lat_as   = MIN(max_lat_as, dem->max_north);
625     start_lon_as = MAX(min_lon_as, dem->min_east);
626     end_lon_as   = MIN(max_lon_as, dem->max_east);
627
628     start_lat = floor(start_lat_as / dem->north_scale) * nscale_deg;
629     end_lat   = ceil (end_lat_as / dem->north_scale) * nscale_deg;
630     start_lon = floor(start_lon_as / dem->east_scale) * escale_deg;
631     end_lon   = ceil (end_lon_as / dem->east_scale) * escale_deg;
632
633     vik_dem_east_north_to_xy ( dem, start_lon_as, start_lat_as, &start_x, &start_y );
634     guint gradient_skip_factor = 1;
635     if(vdl->type == DEM_TYPE_GRADIENT)
636             gradient_skip_factor = skip_factor;
637
638     /* verify sane elev interval */
639     if ( vdl->max_elev <= vdl->min_elev )
640       vdl->max_elev = vdl->min_elev + 1;
641
642     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 ) {
643       // NOTE: ( counter.lon <= end_lon + ESCALE_DEG*SKIP_FACTOR ) is neccessary so in high zoom modes,
644       // the leftmost column does also get drawn, if the center point is out of viewport.
645       if ( x < dem->n_columns ) {
646         column = g_ptr_array_index ( dem->columns, x );
647         // get previous and next column. catch out-of-bound.
648         gint32 new_x = x;
649         new_x -= gradient_skip_factor;
650         if(new_x < 1)
651           prevcolumn = g_ptr_array_index ( dem->columns, x+1);
652         else
653           prevcolumn = g_ptr_array_index ( dem->columns, new_x);
654         new_x = x;
655         new_x += gradient_skip_factor;
656         if(new_x >= dem->n_columns)
657           nextcolumn = g_ptr_array_index ( dem->columns, x-1);
658         else
659           nextcolumn = g_ptr_array_index ( dem->columns, new_x);
660
661         for ( y=start_y, counter.lat = start_lat; counter.lat <= end_lat; counter.lat += nscale_deg * skip_factor, y += skip_factor ) {
662           if ( y > column->n_points )
663             break;
664
665           elev = column->points[y];
666
667           // calculate bounding box for drawing
668           gint box_x, box_y, box_width, box_height;
669           struct LatLon box_c;
670           box_c = counter;
671           box_c.lat += (nscale_deg * skip_factor)/2;
672           box_c.lon -= (escale_deg * skip_factor)/2;
673           vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
674           vik_viewport_coord_to_screen(vp, &tmp, &box_x, &box_y);
675           // catch box at borders
676           if(box_x < 0)
677                   box_x = 0;
678           if(box_y < 0)
679                   box_y = 0;
680           box_c.lat -= nscale_deg * skip_factor;
681           box_c.lon += escale_deg * skip_factor;
682           vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
683           vik_viewport_coord_to_screen(vp, &tmp, &box_width, &box_height);
684           box_width -= box_x;
685           box_height -= box_y;
686           // catch box at borders
687           if(box_width < 0 || box_height < 0)
688             // skip this as is out of the viewport (e.g. zoomed in so this point is way off screen)
689             continue;
690
691           gboolean below_minimum = FALSE;
692           if(vdl->type == DEM_TYPE_HEIGHT) {
693             if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev ) {
694               // Prevent 'elev - vdl->min_elev' from being negative so can safely use as array index
695               elev = ceil ( vdl->min_elev );
696               below_minimum = TRUE;
697             }
698             if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
699               elev = vdl->max_elev;
700           }
701
702           {
703             if(vdl->type == DEM_TYPE_GRADIENT) {
704               if( elev == VIK_DEM_INVALID_ELEVATION ) {
705                 /* don't draw it */
706               } else {
707                 // calculate and sum gradient in all directions
708                 gint16 change = 0;
709                 gint32 new_y;
710
711                 // calculate gradient from height points all around the current one
712                 new_y = y - gradient_skip_factor;
713                 if(new_y < 0)
714                         new_y = y;
715                 change += get_height_difference(elev, prevcolumn->points[new_y]);
716                 change += get_height_difference(elev, column->points[new_y]);
717                 change += get_height_difference(elev, nextcolumn->points[new_y]);
718
719                 change += get_height_difference(elev, prevcolumn->points[y]);
720                 change += get_height_difference(elev, nextcolumn->points[y]);
721
722                 new_y = y + gradient_skip_factor;
723                 if(new_y >= column->n_points)
724                         new_y = y;
725                 change += get_height_difference(elev, prevcolumn->points[new_y]);
726                 change += get_height_difference(elev, column->points[new_y]);
727                 change += get_height_difference(elev, nextcolumn->points[new_y]);
728
729                 change = change / ((skip_factor > 1) ? log(skip_factor) : 0.55); // FIXME: better calc.
730
731                 if(change < vdl->min_elev)
732                   // Prevent 'change - vdl->min_elev' from being negative so can safely use as array index
733                   change = ceil ( vdl->min_elev );
734
735                 if(change > vdl->max_elev)
736                   change = vdl->max_elev;
737
738                 // void vik_viewport_draw_rectangle ( VikViewport *vvp, GdkGC *gc, gboolean filled, gint x1, gint y1, gint x2, gint y2 );
739                 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);
740               }
741             } else {
742               if(vdl->type == DEM_TYPE_HEIGHT) {
743                 if ( elev == VIK_DEM_INVALID_ELEVATION )
744                   ; /* don't draw it */
745                 else if ( elev <= 0 || below_minimum )
746                   /* If 'sea' colour or below the defined mininum draw in the configurable colour */
747                   vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, box_x, box_y, box_width, box_height);
748                 else
749                   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);
750               }
751             }
752           }
753         } /* for y= */
754       }
755     } /* for x= */
756   } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
757     gdouble max_nor, max_eas, min_nor, min_eas;
758     gdouble start_nor, start_eas, end_nor, end_eas;
759
760     gint16 elev;
761
762     guint x, y, start_x, start_y;
763
764     VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
765     struct UTM counter;
766
767     guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 10 ); /* todo: smarter calculation. */
768
769     VikCoord tleft, tright, bleft, bright;
770
771     vik_viewport_screen_to_coord ( vp, 0, 0, &tleft );
772     vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), 0, &tright );
773     vik_viewport_screen_to_coord ( vp, 0, vik_viewport_get_height(vp), &bleft );
774     vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), vik_viewport_get_height(vp), &bright );
775
776
777     vik_coord_convert(&tleft, VIK_COORD_UTM);
778     vik_coord_convert(&tright, VIK_COORD_UTM);
779     vik_coord_convert(&bleft, VIK_COORD_UTM);
780     vik_coord_convert(&bright, VIK_COORD_UTM);
781
782     max_nor = MAX(tleft.north_south, tright.north_south);
783     min_nor = MIN(bleft.north_south, bright.north_south);
784     max_eas = MAX(bright.east_west, tright.east_west);
785     min_eas = MIN(bleft.east_west, tleft.east_west);
786
787     start_nor = MAX(min_nor, dem->min_north);
788     end_nor   = MIN(max_nor, dem->max_north);
789     if ( tleft.utm_zone == dem->utm_zone && bleft.utm_zone == dem->utm_zone
790          && (tleft.utm_letter >= 'N') == (dem->utm_letter >= 'N')
791          && (bleft.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
792       start_eas = MAX(min_eas, dem->min_east);
793     else
794       start_eas = dem->min_east;
795     if ( tright.utm_zone == dem->utm_zone && bright.utm_zone == dem->utm_zone
796          && (tright.utm_letter >= 'N') == (dem->utm_letter >= 'N')
797          && (bright.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
798       end_eas = MIN(max_eas, dem->max_east);
799     else
800       end_eas = dem->max_east;
801
802     start_nor = floor(start_nor / dem->north_scale) * dem->north_scale;
803     end_nor   = ceil (end_nor / dem->north_scale) * dem->north_scale;
804     start_eas = floor(start_eas / dem->east_scale) * dem->east_scale;
805     end_eas   = ceil (end_eas / dem->east_scale) * dem->east_scale;
806
807     vik_dem_east_north_to_xy ( dem, start_eas, start_nor, &start_x, &start_y );
808
809     /* TODO: why start_x and start_y are -1 -- rounding error from above? */
810
811     counter.zone = dem->utm_zone;
812     counter.letter = dem->utm_letter;
813
814     for ( x=start_x, counter.easting = start_eas; counter.easting <= end_eas; counter.easting += dem->east_scale * skip_factor, x += skip_factor ) {
815       if ( x > 0 && x < dem->n_columns ) {
816         column = g_ptr_array_index ( dem->columns, x );
817         for ( y=start_y, counter.northing = start_nor; counter.northing <= end_nor; counter.northing += dem->north_scale * skip_factor, y += skip_factor ) {
818           if ( y > column->n_points )
819             continue;
820           elev = column->points[y];
821           if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev )
822             elev=vdl->min_elev;
823           if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
824             elev=vdl->max_elev;
825
826           {
827             gint a, b;
828             vik_coord_load_from_utm(&tmp, vik_viewport_get_coord_mode(vp), &counter);
829                     vik_viewport_coord_to_screen(vp, &tmp, &a, &b);
830             if ( elev == VIK_DEM_INVALID_ELEVATION )
831               ; /* don't draw it */
832             else if ( elev <= 0 )
833               vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, a-1, b-1, 2, 2 );
834             else
835               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 );
836           }
837         } /* for y= */
838       }
839     } /* for x= */
840   }
841 }
842
843 /* return the continent for the specified lat, lon */
844 /* TODO */
845 static const gchar *srtm_continent_dir ( gint lat, gint lon )
846 {
847   extern const char *_srtm_continent_data[];
848   static GHashTable *srtm_continent = NULL;
849   const gchar *continent;
850   gchar name[16];
851
852   if (!srtm_continent) {
853     const gchar **s;
854
855     srtm_continent = g_hash_table_new(g_str_hash, g_str_equal);
856     s = _srtm_continent_data;
857     while (*s != (gchar *)-1) {
858       continent = *s++;
859       while (*s) {
860         g_hash_table_insert(srtm_continent, (gpointer) *s, (gpointer) continent);
861         s++;
862       }
863       s++;
864     }
865   }
866   g_snprintf(name, sizeof(name), "%c%02d%c%03d",
867                   (lat >= 0) ? 'N' : 'S', ABS(lat),
868                   (lon >= 0) ? 'E' : 'W', ABS(lon));
869
870   return(g_hash_table_lookup(srtm_continent, name));
871 }
872
873 static void dem_layer_draw ( VikDEMLayer *vdl, VikViewport *vp )
874 {
875   GList *dems_iter = vdl->files;
876   VikDEM *dem;
877
878
879   /* search for SRTM3 90m */
880
881   if ( vdl->source == DEM_SOURCE_SRTM )
882     srtm_draw_existence ( vp );
883 #ifdef VIK_CONFIG_DEM24K
884   else if ( vdl->source == DEM_SOURCE_DEM24K )
885     dem24k_draw_existence ( vp );
886 #endif
887
888   while ( dems_iter ) {
889     dem = a_dems_get ( (const char *) (dems_iter->data) );
890     if ( dem )
891       vik_dem_layer_draw_dem ( vdl, vp, dem );
892     dems_iter = dems_iter->next;
893   }
894 }
895
896 static void dem_layer_free ( VikDEMLayer *vdl )
897 {
898   gint i;
899   if ( vdl->gcs )
900     for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
901       g_object_unref ( vdl->gcs[i] );
902   g_free ( vdl->gcs );
903
904   if ( vdl->gcsgradient )
905     for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
906       g_object_unref ( vdl->gcsgradient[i] );
907   g_free ( vdl->gcsgradient );
908
909   a_dems_list_free ( vdl->files );
910 }
911
912 VikDEMLayer *dem_layer_create ( VikViewport *vp )
913 {
914   VikDEMLayer *vdl = dem_layer_new ( vp );
915   gint i;
916   if ( vp ) {
917     /* TODO: share GCS between layers */
918     for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ ) {
919       if ( i > 0 )
920         vdl->gcs[i] = vik_viewport_new_gc ( vp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
921     }
922     for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
923       vdl->gcsgradient[i] = vik_viewport_new_gc ( vp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
924   }
925   return vdl;
926 }
927 /**************************************************************
928  **** SOURCES & DOWNLOADING
929  **************************************************************/
930 typedef struct {
931   gchar *dest;
932   gdouble lat, lon;
933
934   GMutex *mutex;
935   VikDEMLayer *vdl; /* NULL if not alive */
936
937   guint source;
938 } DEMDownloadParams;
939
940
941 /**************************************************
942  *  SOURCE: SRTM                                  *
943  **************************************************/
944
945 static void srtm_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
946 {
947   gint intlat, intlon;
948   const gchar *continent_dir;
949
950   intlat = (int)floor(p->lat);
951   intlon = (int)floor(p->lon);
952   continent_dir = srtm_continent_dir(intlat, intlon);
953
954   if (!continent_dir) {
955     if ( p->vdl ) {
956       gchar *msg = g_strdup_printf ( _("No SRTM data available for %f, %f"), p->lat, p->lon );
957       vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(p->vdl), msg, VIK_STATUSBAR_INFO );
958       g_free ( msg );
959     }
960     return;
961   }
962
963   gchar *src_url = g_strdup_printf("%s/%s/%c%02d%c%03d.hgt.zip",
964                 base_url,
965                 continent_dir,
966                 (intlat >= 0) ? 'N' : 'S',
967                 ABS(intlat),
968                 (intlon >= 0) ? 'E' : 'W',
969                 ABS(intlon) );
970
971   static DownloadFileOptions options = { FALSE, FALSE, NULL, 5, a_check_map_file, NULL, NULL };
972   DownloadResult_t result = a_http_download_get_url ( src_url, NULL, p->dest, &options, NULL );
973   switch ( result ) {
974     case DOWNLOAD_PARAMETERS_ERROR:
975     case DOWNLOAD_CONTENT_ERROR:
976     case DOWNLOAD_HTTP_ERROR: {
977       gchar *msg = g_strdup_printf ( _("DEM download failure for %f, %f"), p->lat, p->lon );
978       vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(p->vdl), msg, VIK_STATUSBAR_INFO );
979       g_free ( msg );
980       break;
981     }
982     case DOWNLOAD_FILE_WRITE_ERROR: {
983       gchar *msg = g_strdup_printf ( _("DEM write failure for %s"), p->dest );
984       vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(p->vdl), msg, VIK_STATUSBAR_INFO );
985       g_free ( msg );
986       break;
987     }
988     case DOWNLOAD_SUCCESS:
989     case DOWNLOAD_NOT_REQUIRED:
990     default:
991       break;
992   }
993   g_free ( src_url );
994 }
995
996 static gchar *srtm_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
997 {
998   gint intlat, intlon;
999   const gchar *continent_dir;
1000
1001   intlat = (int)floor(lat);
1002   intlon = (int)floor(lon);
1003   continent_dir = srtm_continent_dir(intlat, intlon);
1004
1005   if (!continent_dir)
1006     continent_dir = "nowhere";
1007
1008   return g_strdup_printf("srtm3-%s%s%c%02d%c%03d.hgt.zip",
1009                 continent_dir,
1010                 G_DIR_SEPARATOR_S,
1011                 (intlat >= 0) ? 'N' : 'S',
1012                 ABS(intlat),
1013                 (intlon >= 0) ? 'E' : 'W',
1014                 ABS(intlon) );
1015
1016 }
1017
1018 /* TODO: generalize */
1019 static void srtm_draw_existence ( VikViewport *vp )
1020 {
1021   gdouble max_lat, max_lon, min_lat, min_lon;  
1022   gchar buf[strlen(MAPS_CACHE_DIR)+strlen(SRTM_CACHE_TEMPLATE)+30];
1023   gint i, j;
1024
1025   vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
1026
1027   for (i = floor(min_lat); i <= floor(max_lat); i++) {
1028     for (j = floor(min_lon); j <= floor(max_lon); j++) {
1029       const gchar *continent_dir;
1030       if ((continent_dir = srtm_continent_dir(i, j)) == NULL)
1031         continue;
1032       g_snprintf(buf, sizeof(buf), SRTM_CACHE_TEMPLATE,
1033                 MAPS_CACHE_DIR,
1034                 continent_dir,
1035                 G_DIR_SEPARATOR_S,
1036                 (i >= 0) ? 'N' : 'S',
1037                 ABS(i),
1038                 (j >= 0) ? 'E' : 'W',
1039                 ABS(j) );
1040       if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1041         VikCoord ne, sw;
1042         gint x1, y1, x2, y2;
1043         sw.north_south = i;
1044         sw.east_west = j;
1045         sw.mode = VIK_COORD_LATLON;
1046         ne.north_south = i+1;
1047         ne.east_west = j+1;
1048         ne.mode = VIK_COORD_LATLON;
1049         vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
1050         vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
1051         if ( x1 < 0 ) x1 = 0;
1052         if ( y2 < 0 ) y2 = 0;
1053         vik_viewport_draw_rectangle ( vp, gtk_widget_get_style(GTK_WIDGET(vp))->black_gc,
1054                 FALSE, x1, y2, x2-x1, y1-y2 );
1055       }
1056     }
1057   }
1058 }
1059
1060
1061 /**************************************************
1062  *  SOURCE: USGS 24K                              *
1063  **************************************************/
1064
1065 #ifdef VIK_CONFIG_DEM24K
1066
1067 static void dem24k_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
1068 {
1069   /* TODO: dest dir */
1070   gchar *cmdline = g_strdup_printf("%s %.03f %.03f",
1071         DEM24K_DOWNLOAD_SCRIPT,
1072         floor(p->lat*8)/8,
1073         ceil(p->lon*8)/8 );
1074   /* FIX: don't use system, use execv or something. check for existence */
1075   system(cmdline);
1076   g_free ( cmdline );
1077 }
1078
1079 static gchar *dem24k_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
1080 {
1081   return g_strdup_printf("dem24k/%d/%d/%.03f,%.03f.dem",
1082         (gint) lat,
1083         (gint) lon,
1084         floor(lat*8)/8,
1085         ceil(lon*8)/8);
1086 }
1087
1088 /* TODO: generalize */
1089 static void dem24k_draw_existence ( VikViewport *vp )
1090 {
1091   gdouble max_lat, max_lon, min_lat, min_lon;  
1092   gchar buf[strlen(MAPS_CACHE_DIR)+40];
1093   gdouble i, j;
1094
1095   vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
1096
1097   for (i = floor(min_lat*8)/8; i <= floor(max_lat*8)/8; i+=0.125) {
1098     /* check lat dir first -- faster */
1099     g_snprintf(buf, sizeof(buf), "%sdem24k/%d/",
1100         MAPS_CACHE_DIR,
1101         (gint) i );
1102     if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1103       continue;
1104     for (j = floor(min_lon*8)/8; j <= floor(max_lon*8)/8; j+=0.125) {
1105       /* check lon dir first -- faster */
1106       g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/",
1107         MAPS_CACHE_DIR,
1108         (gint) i,
1109         (gint) j );
1110       if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1111         continue;
1112       g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/%.03f,%.03f.dem",
1113                 MAPS_CACHE_DIR,
1114                 (gint) i,
1115                 (gint) j,
1116                 floor(i*8)/8,
1117                 floor(j*8)/8 );
1118       if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1119         VikCoord ne, sw;
1120         gint x1, y1, x2, y2;
1121         sw.north_south = i;
1122         sw.east_west = j-0.125;
1123         sw.mode = VIK_COORD_LATLON;
1124         ne.north_south = i+0.125;
1125         ne.east_west = j;
1126         ne.mode = VIK_COORD_LATLON;
1127         vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
1128         vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
1129         if ( x1 < 0 ) x1 = 0;
1130         if ( y2 < 0 ) y2 = 0;
1131         vik_viewport_draw_rectangle ( vp, gtk_widget_get_style(GTK_WIDGET(vp))->black_gc,
1132                 FALSE, x1, y2, x2-x1, y1-y2 );
1133       }
1134     }
1135   }
1136 }
1137 #endif
1138
1139 /**************************************************
1140  *   SOURCES -- DOWNLOADING & IMPORTING TOOL      *
1141  **************************************************
1142  */
1143
1144 static void weak_ref_cb ( gpointer ptr, GObject * dead_vdl )
1145 {
1146   DEMDownloadParams *p = ptr;
1147   g_mutex_lock ( p->mutex );
1148   p->vdl = NULL;
1149   g_mutex_unlock ( p->mutex );
1150 }
1151
1152 /* Try to add file full_path.
1153  * filename will be copied.
1154  * returns FALSE if file does not exists, TRUE otherwise.
1155  */
1156 static gboolean dem_layer_add_file ( VikDEMLayer *vdl, const gchar *filename )
1157 {
1158   if ( g_file_test(filename, G_FILE_TEST_EXISTS) == TRUE ) {
1159     /* only load if file size is not 0 (not in progress) */
1160     GStatBuf sb;
1161     (void)g_stat ( filename, &sb );
1162     if ( sb.st_size ) {
1163       gchar *duped_path = g_strdup(filename);
1164       vdl->files = g_list_prepend ( vdl->files, duped_path );
1165       a_dems_load ( duped_path );
1166       g_debug("%s: %s", __FUNCTION__, duped_path);
1167     }
1168     return TRUE;
1169   } else
1170     return FALSE;
1171 }
1172
1173 static void dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
1174 {
1175   if ( p->source == DEM_SOURCE_SRTM )
1176     srtm_dem_download_thread ( p, threaddata );
1177 #ifdef VIK_CONFIG_DEM24K
1178   else if ( p->source == DEM_SOURCE_DEM24K )
1179     dem24k_dem_download_thread ( p, threaddata );
1180 #endif
1181   else
1182     return;
1183
1184   //gdk_threads_enter();
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 from background thread
1191   }
1192   g_mutex_unlock ( p->mutex );
1193   //gdk_threads_leave();
1194 }
1195
1196
1197 static void free_dem_download_params ( DEMDownloadParams *p )
1198 {
1199   vik_mutex_free ( p->mutex );
1200   g_free ( p->dest );
1201   g_free ( p );
1202 }
1203
1204 static gpointer dem_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1205 {
1206   return vvp;
1207 }
1208
1209 /**
1210  * Display a simple dialog with information about the DEM file at this location
1211  */
1212 static void dem_layer_file_info ( GtkWidget *widget, struct LatLon *ll )
1213 {
1214   gint intlat, intlon;
1215   const gchar *continent_dir;
1216
1217   intlat = (int)floor(ll->lat);
1218   intlon = (int)floor(ll->lon);
1219   continent_dir = srtm_continent_dir(intlat, intlon);
1220
1221   gchar *source = NULL;
1222   if ( continent_dir )
1223     source = g_strdup_printf ( "%s/%s/%c%02d%c%03d.hgt.zip",
1224                                base_url,
1225                                continent_dir,
1226                                (intlat >= 0) ? 'N' : 'S',
1227                                ABS(intlat),
1228                                (intlon >= 0) ? 'E' : 'W',
1229                                ABS(intlon) );
1230   else
1231     // Probably not over any land...
1232     source = g_strdup ( _("No DEM File Available") );
1233
1234   gchar *filename = NULL;
1235   gchar *dem_file = NULL;
1236 #ifdef VIK_CONFIG_DEM24K
1237   dem_file = dem24k_lat_lon_to_dest_fn ( ll->lat, ll->lon );
1238 #else
1239   dem_file = srtm_lat_lon_to_dest_fn ( ll->lat, ll->lon );
1240 #endif
1241   gchar *message = NULL;
1242
1243   filename = g_strdup_printf ( "%s%s", MAPS_CACHE_DIR, dem_file );
1244
1245   if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1246     // Get some timestamp information of the file
1247     GStatBuf stat_buf;
1248     if ( g_stat ( filename, &stat_buf ) == 0 ) {
1249       gchar time_buf[64];
1250       strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
1251       message = g_strdup_printf ( _("\nSource: %s\n\nDEM File: %s\nDEM File Timestamp: %s"), source, filename, time_buf );
1252     }
1253   }
1254   else
1255     message = g_strdup_printf ( _("Source: %s\n\nNo DEM File!"), source );
1256
1257   // Show the info
1258   a_dialog_info_msg ( GTK_WINDOW(gtk_widget_get_toplevel(widget)), message );
1259
1260   g_free ( message );
1261   g_free ( source );
1262   g_free ( dem_file );
1263   g_free ( filename );
1264 }
1265
1266 static gboolean dem_layer_download_release ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1267 {
1268   VikCoord coord;
1269   static struct LatLon ll;
1270
1271   gchar *full_path;
1272   gchar *dem_file = NULL;
1273
1274   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1275   vik_coord_to_latlon ( &coord, &ll );
1276
1277   
1278   if ( vdl->source == DEM_SOURCE_SRTM )
1279     dem_file = srtm_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1280 #ifdef VIK_CONFIG_DEM24K
1281   else if ( vdl->source == DEM_SOURCE_DEM24K )
1282     dem_file = dem24k_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1283 #endif
1284
1285   if ( ! dem_file )
1286     return TRUE;
1287
1288   full_path = g_strdup_printf("%s%s", MAPS_CACHE_DIR, dem_file );
1289
1290   g_debug("%s: %s", __FUNCTION__, full_path);
1291
1292   if ( event->button == 1 ) {
1293     // TODO: check if already in filelist
1294     if ( ! dem_layer_add_file(vdl, full_path) ) {
1295       gchar *tmp = g_strdup_printf ( _("Downloading DEM %s"), dem_file );
1296       DEMDownloadParams *p = g_malloc(sizeof(DEMDownloadParams));
1297       p->dest = g_strdup(full_path);
1298       p->lat = ll.lat;
1299       p->lon = ll.lon;
1300       p->vdl = vdl;
1301       p->mutex = vik_mutex_new();
1302       p->source = vdl->source;
1303       g_object_weak_ref(G_OBJECT(p->vdl), weak_ref_cb, p );
1304
1305       a_background_thread ( BACKGROUND_POOL_REMOTE,
1306                             VIK_GTK_WINDOW_FROM_LAYER(vdl), tmp,
1307                             (vik_thr_func) dem_download_thread, p,
1308                             (vik_thr_free_func) free_dem_download_params, NULL, 1 );
1309
1310       g_free ( tmp );
1311     }
1312     else
1313       vik_layer_emit_update ( VIK_LAYER(vdl) );
1314   }
1315   else {
1316     if ( !vdl->right_click_menu ) {
1317       GtkWidget *item;
1318       vdl->right_click_menu = GTK_MENU ( gtk_menu_new () );
1319
1320       item = gtk_image_menu_item_new_with_mnemonic ( _("_Show DEM File Information") );
1321       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
1322       g_signal_connect ( G_OBJECT(item), "activate", G_CALLBACK(dem_layer_file_info), &ll );
1323       gtk_menu_shell_append (GTK_MENU_SHELL(vdl->right_click_menu), item);
1324     }
1325
1326     gtk_menu_popup ( vdl->right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1327     gtk_widget_show_all ( GTK_WIDGET(vdl->right_click_menu) );
1328   }
1329
1330   g_free ( dem_file );
1331   g_free ( full_path );
1332
1333   return TRUE;
1334 }
1335
1336 static gboolean dem_layer_download_click ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1337 {
1338 /* choose & keep track of cache dir
1339  * download in background thread
1340  * download over area */
1341   return TRUE;
1342 }
1343
1344