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