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