]> git.street.me.uk Git - andy/viking.git/blob - src/vikdemlayer.c
[WINDOWS] Fix reading zip files on Windows due to compiler alignment defaults changing.
[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   }
546
547   if ( (max_lat > dem_northeast.lat && min_lat > dem_northeast.lat) ||
548        (max_lat < dem_southwest.lat && min_lat < dem_southwest.lat) )
549     return;
550   else if ( (max_lon > dem_northeast.lon && min_lon > dem_northeast.lon) ||
551             (max_lon < dem_southwest.lon && min_lon < dem_southwest.lon) )
552     return;
553   /* else they overlap */
554
555   /**** End Overlap Check ****/
556   /* boxes to show where we have DEM instead of actually drawing the DEM.
557    * useful if we want to see what areas we have coverage for (if we want
558    * to get elevation data for a track) but don't want to cover the map.
559    */
560
561   #if 0
562   /* draw a box if a DEM is loaded. in future I'd like to add an option for this
563    * this is useful if we want to see what areas we have dem for but don't want to
564    * cover the map (or maybe we just need translucent DEM?) */
565   {
566     VikCoord demne, demsw;
567     gint x1, y1, x2, y2;
568     vik_coord_load_from_latlon(&demne, vik_viewport_get_coord_mode(vp), &dem_northeast);
569     vik_coord_load_from_latlon(&demsw, vik_viewport_get_coord_mode(vp), &dem_southwest);
570
571     vik_viewport_coord_to_screen ( vp, &demne, &x1, &y1 );
572     vik_viewport_coord_to_screen ( vp, &demsw, &x2, &y2 );
573
574     if ( x1 > vik_viewport_get_width(vp) ) x1=vik_viewport_get_width(vp);
575     if ( y2 > vik_viewport_get_height(vp) ) y2=vik_viewport_get_height(vp);
576     if ( x2 < 0 ) x2 = 0;
577     if ( y1 < 0 ) y1 = 0;
578     vik_viewport_draw_rectangle ( vp, gtk_widget_get_style(GTK_WIDGET(vp))->black_gc,
579         FALSE, x2, y1, x1-x2, y2-y1 );
580     return;
581   }
582   #endif
583
584   if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
585     VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
586
587     gdouble max_lat_as, max_lon_as, min_lat_as, min_lon_as;  
588     gdouble start_lat_as, end_lat_as, start_lon_as, end_lon_as;
589
590     gdouble start_lat, end_lat, start_lon, end_lon;
591
592     struct LatLon counter;
593
594     guint x, y, start_x, start_y;
595
596     gint16 elev;
597
598     guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 80 ); /* todo: smarter calculation. */
599
600     gdouble nscale_deg = dem->north_scale / ((gdouble) 3600);
601     gdouble escale_deg = dem->east_scale / ((gdouble) 3600);
602
603     max_lat_as = max_lat * 3600;
604     min_lat_as = min_lat * 3600;
605     max_lon_as = max_lon * 3600;
606     min_lon_as = min_lon * 3600;
607
608     start_lat_as = MAX(min_lat_as, dem->min_north);
609     end_lat_as   = MIN(max_lat_as, dem->max_north);
610     start_lon_as = MAX(min_lon_as, dem->min_east);
611     end_lon_as   = MIN(max_lon_as, dem->max_east);
612
613     start_lat = floor(start_lat_as / dem->north_scale) * nscale_deg;
614     end_lat   = ceil (end_lat_as / dem->north_scale) * nscale_deg;
615     start_lon = floor(start_lon_as / dem->east_scale) * escale_deg;
616     end_lon   = ceil (end_lon_as / dem->east_scale) * escale_deg;
617
618     vik_dem_east_north_to_xy ( dem, start_lon_as, start_lat_as, &start_x, &start_y );
619     guint gradient_skip_factor = 1;
620     if(vdl->type == DEM_TYPE_GRADIENT)
621             gradient_skip_factor = skip_factor;
622
623     /* verify sane elev interval */
624     if ( vdl->max_elev <= vdl->min_elev )
625       vdl->max_elev = vdl->min_elev + 1;
626
627     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 ) {
628       // NOTE: ( counter.lon <= end_lon + ESCALE_DEG*SKIP_FACTOR ) is neccessary so in high zoom modes,
629       // the leftmost column does also get drawn, if the center point is out of viewport.
630       if ( x < dem->n_columns ) {
631         column = g_ptr_array_index ( dem->columns, x );
632         // get previous and next column. catch out-of-bound.
633         gint32 new_x = x;
634         new_x -= gradient_skip_factor;
635         if(new_x < 1)
636           prevcolumn = g_ptr_array_index ( dem->columns, x+1);
637         else
638           prevcolumn = g_ptr_array_index ( dem->columns, new_x);
639         new_x = x;
640         new_x += gradient_skip_factor;
641         if(new_x >= dem->n_columns)
642           nextcolumn = g_ptr_array_index ( dem->columns, x-1);
643         else
644           nextcolumn = g_ptr_array_index ( dem->columns, new_x);
645
646         for ( y=start_y, counter.lat = start_lat; counter.lat <= end_lat; counter.lat += nscale_deg * skip_factor, y += skip_factor ) {
647           if ( y > column->n_points )
648             break;
649
650           elev = column->points[y];
651
652           // calculate bounding box for drawing
653           gint box_x, box_y, box_width, box_height;
654           struct LatLon box_c;
655           box_c = counter;
656           box_c.lat += (nscale_deg * skip_factor)/2;
657           box_c.lon -= (escale_deg * skip_factor)/2;
658           vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
659           vik_viewport_coord_to_screen(vp, &tmp, &box_x, &box_y);
660           // catch box at borders
661           if(box_x < 0)
662                   box_x = 0;
663           if(box_y < 0)
664                   box_y = 0;
665           box_c.lat -= nscale_deg * skip_factor;
666           box_c.lon += escale_deg * skip_factor;
667           vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
668           vik_viewport_coord_to_screen(vp, &tmp, &box_width, &box_height);
669           box_width -= box_x;
670           box_height -= box_y;
671           // catch box at borders
672           if(box_width < 0 || box_height < 0)
673                   continue; // skip this. this is out of our viewport anyway. FIXME: why?
674
675           gboolean below_minimum = FALSE;
676           if(vdl->type == DEM_TYPE_HEIGHT) {
677             if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev ) {
678               // Prevent 'elev - vdl->min_elev' from being negative so can safely use as array index
679               elev = ceil ( vdl->min_elev );
680               below_minimum = TRUE;
681             }
682             if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
683               elev = vdl->max_elev;
684           }
685
686           {
687             if(box_width < 0 || box_height < 0) // FIXME: why does this happen?
688               continue;
689
690             if(vdl->type == DEM_TYPE_GRADIENT) {
691               if( elev == VIK_DEM_INVALID_ELEVATION ) {
692                 /* don't draw it */
693               } else {
694                 // calculate and sum gradient in all directions
695                 gint16 change = 0;
696                 gint32 new_y;
697
698                 // calculate gradient from height points all around the current one
699                 new_y = y - gradient_skip_factor;
700                 if(new_y < 0)
701                         new_y = y;
702                 change += get_height_difference(elev, prevcolumn->points[new_y]);
703                 change += get_height_difference(elev, column->points[new_y]);
704                 change += get_height_difference(elev, nextcolumn->points[new_y]);
705
706                 change += get_height_difference(elev, prevcolumn->points[y]);
707                 change += get_height_difference(elev, nextcolumn->points[y]);
708
709                 new_y = y + gradient_skip_factor;
710                 if(new_y >= column->n_points)
711                         new_y = y;
712                 change += get_height_difference(elev, prevcolumn->points[new_y]);
713                 change += get_height_difference(elev, column->points[new_y]);
714                 change += get_height_difference(elev, nextcolumn->points[new_y]);
715
716                 change = change / ((skip_factor > 1) ? log(skip_factor) : 0.55); // FIXME: better calc.
717
718                 if(change < vdl->min_elev)
719                   // Prevent 'change - vdl->min_elev' from being negative so can safely use as array index
720                   change = ceil ( vdl->min_elev );
721
722                 if(change > vdl->max_elev)
723                   change = vdl->max_elev;
724
725                 // void vik_viewport_draw_rectangle ( VikViewport *vvp, GdkGC *gc, gboolean filled, gint x1, gint y1, gint x2, gint y2 );
726                 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);
727               }
728             } else {
729               if(vdl->type == DEM_TYPE_HEIGHT) {
730                 if ( elev == VIK_DEM_INVALID_ELEVATION )
731                   ; /* don't draw it */
732                 else if ( elev <= 0 || below_minimum )
733                   /* If 'sea' colour or below the defined mininum draw in the configurable colour */
734                   vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, box_x, box_y, box_width, box_height);
735                 else
736                   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);
737               }
738             }
739           }
740         } /* for y= */
741       }
742     } /* for x= */
743   } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
744     gdouble max_nor, max_eas, min_nor, min_eas;
745     gdouble start_nor, start_eas, end_nor, end_eas;
746
747     gint16 elev;
748
749     guint x, y, start_x, start_y;
750
751     VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
752     struct UTM counter;
753
754     guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 10 ); /* todo: smarter calculation. */
755
756     VikCoord tleft, tright, bleft, bright;
757
758     vik_viewport_screen_to_coord ( vp, 0, 0, &tleft );
759     vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), 0, &tright );
760     vik_viewport_screen_to_coord ( vp, 0, vik_viewport_get_height(vp), &bleft );
761     vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), vik_viewport_get_height(vp), &bright );
762
763
764     vik_coord_convert(&tleft, VIK_COORD_UTM);
765     vik_coord_convert(&tright, VIK_COORD_UTM);
766     vik_coord_convert(&bleft, VIK_COORD_UTM);
767     vik_coord_convert(&bright, VIK_COORD_UTM);
768
769     max_nor = MAX(tleft.north_south, tright.north_south);
770     min_nor = MIN(bleft.north_south, bright.north_south);
771     max_eas = MAX(bright.east_west, tright.east_west);
772     min_eas = MIN(bleft.east_west, tleft.east_west);
773
774     start_nor = MAX(min_nor, dem->min_north);
775     end_nor   = MIN(max_nor, dem->max_north);
776     if ( tleft.utm_zone == dem->utm_zone && bleft.utm_zone == dem->utm_zone
777          && (tleft.utm_letter >= 'N') == (dem->utm_letter >= 'N')
778          && (bleft.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
779       start_eas = MAX(min_eas, dem->min_east);
780     else
781       start_eas = dem->min_east;
782     if ( tright.utm_zone == dem->utm_zone && bright.utm_zone == dem->utm_zone
783          && (tright.utm_letter >= 'N') == (dem->utm_letter >= 'N')
784          && (bright.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
785       end_eas = MIN(max_eas, dem->max_east);
786     else
787       end_eas = dem->max_east;
788
789     start_nor = floor(start_nor / dem->north_scale) * dem->north_scale;
790     end_nor   = ceil (end_nor / dem->north_scale) * dem->north_scale;
791     start_eas = floor(start_eas / dem->east_scale) * dem->east_scale;
792     end_eas   = ceil (end_eas / dem->east_scale) * dem->east_scale;
793
794     vik_dem_east_north_to_xy ( dem, start_eas, start_nor, &start_x, &start_y );
795
796     /* TODO: why start_x and start_y are -1 -- rounding error from above? */
797
798     counter.zone = dem->utm_zone;
799     counter.letter = dem->utm_letter;
800
801     for ( x=start_x, counter.easting = start_eas; counter.easting <= end_eas; counter.easting += dem->east_scale * skip_factor, x += skip_factor ) {
802       if ( x > 0 && x < dem->n_columns ) {
803         column = g_ptr_array_index ( dem->columns, x );
804         for ( y=start_y, counter.northing = start_nor; counter.northing <= end_nor; counter.northing += dem->north_scale * skip_factor, y += skip_factor ) {
805           if ( y > column->n_points )
806             continue;
807           elev = column->points[y];
808           if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev )
809             elev=vdl->min_elev;
810           if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
811             elev=vdl->max_elev;
812
813           {
814             gint a, b;
815             vik_coord_load_from_utm(&tmp, vik_viewport_get_coord_mode(vp), &counter);
816                     vik_viewport_coord_to_screen(vp, &tmp, &a, &b);
817             if ( elev == VIK_DEM_INVALID_ELEVATION )
818               ; /* don't draw it */
819             else if ( elev <= 0 )
820               vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, a-1, b-1, 2, 2 );
821             else
822               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 );
823           }
824         } /* for y= */
825       }
826     } /* for x= */
827   }
828 }
829
830 /* return the continent for the specified lat, lon */
831 /* TODO */
832 static const gchar *srtm_continent_dir ( gint lat, gint lon )
833 {
834   extern const char *_srtm_continent_data[];
835   static GHashTable *srtm_continent = NULL;
836   const gchar *continent;
837   gchar name[16];
838
839   if (!srtm_continent) {
840     const gchar **s;
841
842     srtm_continent = g_hash_table_new(g_str_hash, g_str_equal);
843     s = _srtm_continent_data;
844     while (*s != (gchar *)-1) {
845       continent = *s++;
846       while (*s) {
847         g_hash_table_insert(srtm_continent, (gpointer) *s, (gpointer) continent);
848         s++;
849       }
850       s++;
851     }
852   }
853   g_snprintf(name, sizeof(name), "%c%02d%c%03d",
854                   (lat >= 0) ? 'N' : 'S', ABS(lat),
855                   (lon >= 0) ? 'E' : 'W', ABS(lon));
856
857   return(g_hash_table_lookup(srtm_continent, name));
858 }
859
860 static void dem_layer_draw ( VikDEMLayer *vdl, VikViewport *vp )
861 {
862   GList *dems_iter = vdl->files;
863   VikDEM *dem;
864
865
866   /* search for SRTM3 90m */
867
868   if ( vdl->source == DEM_SOURCE_SRTM )
869     srtm_draw_existence ( vp );
870 #ifdef VIK_CONFIG_DEM24K
871   else if ( vdl->source == DEM_SOURCE_DEM24K )
872     dem24k_draw_existence ( vp );
873 #endif
874
875   while ( dems_iter ) {
876     dem = a_dems_get ( (const char *) (dems_iter->data) );
877     if ( dem )
878       vik_dem_layer_draw_dem ( vdl, vp, dem );
879     dems_iter = dems_iter->next;
880   }
881 }
882
883 static void dem_layer_free ( VikDEMLayer *vdl )
884 {
885   gint i;
886   if ( vdl->gcs )
887     for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
888       g_object_unref ( vdl->gcs[i] );
889   g_free ( vdl->gcs );
890
891   if ( vdl->gcsgradient )
892     for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
893       g_object_unref ( vdl->gcsgradient[i] );
894   g_free ( vdl->gcsgradient );
895
896   a_dems_list_free ( vdl->files );
897 }
898
899 VikDEMLayer *dem_layer_create ( VikViewport *vp )
900 {
901   VikDEMLayer *vdl = dem_layer_new ( vp );
902   gint i;
903   if ( vp ) {
904     /* TODO: share GCS between layers */
905     for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ ) {
906       if ( i > 0 )
907         vdl->gcs[i] = vik_viewport_new_gc ( vp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
908     }
909     for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
910       vdl->gcsgradient[i] = vik_viewport_new_gc ( vp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
911   }
912   return vdl;
913 }
914 /**************************************************************
915  **** SOURCES & DOWNLOADING
916  **************************************************************/
917 typedef struct {
918   gchar *dest;
919   gdouble lat, lon;
920
921   GMutex *mutex;
922   VikDEMLayer *vdl; /* NULL if not alive */
923
924   guint source;
925 } DEMDownloadParams;
926
927
928 /**************************************************
929  *  SOURCE: SRTM                                  *
930  **************************************************/
931
932 static void srtm_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
933 {
934   gint intlat, intlon;
935   const gchar *continent_dir;
936
937   intlat = (int)floor(p->lat);
938   intlon = (int)floor(p->lon);
939   continent_dir = srtm_continent_dir(intlat, intlon);
940
941   if (!continent_dir) {
942     if ( p->vdl ) {
943       gchar *msg = g_strdup_printf ( _("No SRTM data available for %f, %f"), p->lat, p->lon );
944       vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(p->vdl), msg, VIK_STATUSBAR_INFO );
945       g_free ( msg );
946     }
947     return;
948   }
949
950   gchar *src_fn = g_strdup_printf("%s%s/%c%02d%c%03d.hgt.zip",
951                 SRTM_HTTP_URI,
952                 continent_dir,
953                 (intlat >= 0) ? 'N' : 'S',
954                 ABS(intlat),
955                 (intlon >= 0) ? 'E' : 'W',
956                 ABS(intlon) );
957
958   static DownloadMapOptions options = { FALSE, FALSE, NULL, 0, a_check_map_file, NULL, NULL };
959   DownloadResult_t result = a_http_download_get_url ( SRTM_HTTP_SITE, src_fn, p->dest, &options, NULL );
960   switch ( result ) {
961     case DOWNLOAD_CONTENT_ERROR:
962     case DOWNLOAD_HTTP_ERROR: {
963       gchar *msg = g_strdup_printf ( _("DEM download failure for %f, %f"), p->lat, p->lon );
964       vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(p->vdl), msg, VIK_STATUSBAR_INFO );
965       g_free ( msg );
966       break;
967     }
968     case DOWNLOAD_FILE_WRITE_ERROR: {
969       gchar *msg = g_strdup_printf ( _("DEM write failure for %s"), p->dest );
970       vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(p->vdl), msg, VIK_STATUSBAR_INFO );
971       g_free ( msg );
972       break;
973     }
974     case DOWNLOAD_SUCCESS:
975     case DOWNLOAD_NOT_REQUIRED:
976     default:
977       break;
978   }
979   g_free ( src_fn );
980 }
981
982 static gchar *srtm_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
983 {
984   gint intlat, intlon;
985   const gchar *continent_dir;
986
987   intlat = (int)floor(lat);
988   intlon = (int)floor(lon);
989   continent_dir = srtm_continent_dir(intlat, intlon);
990
991   if (!continent_dir)
992     continent_dir = "nowhere";
993
994   return g_strdup_printf("srtm3-%s%s%c%02d%c%03d.hgt.zip",
995                 continent_dir,
996                 G_DIR_SEPARATOR_S,
997                 (intlat >= 0) ? 'N' : 'S',
998                 ABS(intlat),
999                 (intlon >= 0) ? 'E' : 'W',
1000                 ABS(intlon) );
1001
1002 }
1003
1004 /* TODO: generalize */
1005 static void srtm_draw_existence ( VikViewport *vp )
1006 {
1007   gdouble max_lat, max_lon, min_lat, min_lon;  
1008   gchar buf[strlen(MAPS_CACHE_DIR)+strlen(SRTM_CACHE_TEMPLATE)+30];
1009   gint i, j;
1010
1011   vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
1012
1013   for (i = floor(min_lat); i <= floor(max_lat); i++) {
1014     for (j = floor(min_lon); j <= floor(max_lon); j++) {
1015       const gchar *continent_dir;
1016       if ((continent_dir = srtm_continent_dir(i, j)) == NULL)
1017         continue;
1018       g_snprintf(buf, sizeof(buf), SRTM_CACHE_TEMPLATE,
1019                 MAPS_CACHE_DIR,
1020                 continent_dir,
1021                 G_DIR_SEPARATOR_S,
1022                 (i >= 0) ? 'N' : 'S',
1023                 ABS(i),
1024                 (j >= 0) ? 'E' : 'W',
1025                 ABS(j) );
1026       if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1027         VikCoord ne, sw;
1028         gint x1, y1, x2, y2;
1029         sw.north_south = i;
1030         sw.east_west = j;
1031         sw.mode = VIK_COORD_LATLON;
1032         ne.north_south = i+1;
1033         ne.east_west = j+1;
1034         ne.mode = VIK_COORD_LATLON;
1035         vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
1036         vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
1037         if ( x1 < 0 ) x1 = 0;
1038         if ( y2 < 0 ) y2 = 0;
1039         vik_viewport_draw_rectangle ( vp, gtk_widget_get_style(GTK_WIDGET(vp))->black_gc,
1040                 FALSE, x1, y2, x2-x1, y1-y2 );
1041       }
1042     }
1043   }
1044 }
1045
1046
1047 /**************************************************
1048  *  SOURCE: USGS 24K                              *
1049  **************************************************/
1050
1051 #ifdef VIK_CONFIG_DEM24K
1052
1053 static void dem24k_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
1054 {
1055   /* TODO: dest dir */
1056   gchar *cmdline = g_strdup_printf("%s %.03f %.03f",
1057         DEM24K_DOWNLOAD_SCRIPT,
1058         floor(p->lat*8)/8,
1059         ceil(p->lon*8)/8 );
1060   /* FIX: don't use system, use execv or something. check for existence */
1061   system(cmdline);
1062   g_free ( cmdline );
1063 }
1064
1065 static gchar *dem24k_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
1066 {
1067   return g_strdup_printf("dem24k/%d/%d/%.03f,%.03f.dem",
1068         (gint) lat,
1069         (gint) lon,
1070         floor(lat*8)/8,
1071         ceil(lon*8)/8);
1072 }
1073
1074 /* TODO: generalize */
1075 static void dem24k_draw_existence ( VikViewport *vp )
1076 {
1077   gdouble max_lat, max_lon, min_lat, min_lon;  
1078   gchar buf[strlen(MAPS_CACHE_DIR)+40];
1079   gdouble i, j;
1080
1081   vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
1082
1083   for (i = floor(min_lat*8)/8; i <= floor(max_lat*8)/8; i+=0.125) {
1084     /* check lat dir first -- faster */
1085     g_snprintf(buf, sizeof(buf), "%sdem24k/%d/",
1086         MAPS_CACHE_DIR,
1087         (gint) i );
1088     if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1089       continue;
1090     for (j = floor(min_lon*8)/8; j <= floor(max_lon*8)/8; j+=0.125) {
1091       /* check lon dir first -- faster */
1092       g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/",
1093         MAPS_CACHE_DIR,
1094         (gint) i,
1095         (gint) j );
1096       if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1097         continue;
1098       g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/%.03f,%.03f.dem",
1099                 MAPS_CACHE_DIR,
1100                 (gint) i,
1101                 (gint) j,
1102                 floor(i*8)/8,
1103                 floor(j*8)/8 );
1104       if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1105         VikCoord ne, sw;
1106         gint x1, y1, x2, y2;
1107         sw.north_south = i;
1108         sw.east_west = j-0.125;
1109         sw.mode = VIK_COORD_LATLON;
1110         ne.north_south = i+0.125;
1111         ne.east_west = j;
1112         ne.mode = VIK_COORD_LATLON;
1113         vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
1114         vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
1115         if ( x1 < 0 ) x1 = 0;
1116         if ( y2 < 0 ) y2 = 0;
1117         vik_viewport_draw_rectangle ( vp, gtk_widget_get_style(GTK_WIDGET(vp))->black_gc,
1118                 FALSE, x1, y2, x2-x1, y1-y2 );
1119       }
1120     }
1121   }
1122 }
1123 #endif
1124
1125 /**************************************************
1126  *   SOURCES -- DOWNLOADING & IMPORTING TOOL      *
1127  **************************************************
1128  */
1129
1130 static void weak_ref_cb ( gpointer ptr, GObject * dead_vdl )
1131 {
1132   DEMDownloadParams *p = ptr;
1133   g_mutex_lock ( p->mutex );
1134   p->vdl = NULL;
1135   g_mutex_unlock ( p->mutex );
1136 }
1137
1138 /* Try to add file full_path.
1139  * filename will be copied.
1140  * returns FALSE if file does not exists, TRUE otherwise.
1141  */
1142 static gboolean dem_layer_add_file ( VikDEMLayer *vdl, const gchar *filename )
1143 {
1144   if ( g_file_test(filename, G_FILE_TEST_EXISTS) == TRUE ) {
1145     /* only load if file size is not 0 (not in progress) */
1146     struct stat sb;
1147     stat ( filename, &sb );
1148     if ( sb.st_size ) {
1149       gchar *duped_path = g_strdup(filename);
1150       vdl->files = g_list_prepend ( vdl->files, duped_path );
1151       a_dems_load ( duped_path );
1152       g_debug("%s: %s", __FUNCTION__, duped_path);
1153     }
1154     return TRUE;
1155   } else
1156     return FALSE;
1157 }
1158
1159 static void dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
1160 {
1161   if ( p->source == DEM_SOURCE_SRTM )
1162     srtm_dem_download_thread ( p, threaddata );
1163 #ifdef VIK_CONFIG_DEM24K
1164   else if ( p->source == DEM_SOURCE_DEM24K )
1165     dem24k_dem_download_thread ( p, threaddata );
1166 #endif
1167   else
1168     return;
1169
1170   //gdk_threads_enter();
1171   g_mutex_lock ( p->mutex );
1172   if ( p->vdl ) {
1173     g_object_weak_unref ( G_OBJECT(p->vdl), weak_ref_cb, p );
1174
1175     if ( dem_layer_add_file ( p->vdl, p->dest ) )
1176       vik_layer_emit_update ( VIK_LAYER(p->vdl) ); // NB update from background thread
1177   }
1178   g_mutex_unlock ( p->mutex );
1179   //gdk_threads_leave();
1180 }
1181
1182
1183 static void free_dem_download_params ( DEMDownloadParams *p )
1184 {
1185   vik_mutex_free ( p->mutex );
1186   g_free ( p->dest );
1187   g_free ( p );
1188 }
1189
1190 static gpointer dem_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1191 {
1192   return vvp;
1193 }
1194
1195 /**
1196  * Display a simple dialog with information about the DEM file at this location
1197  */
1198 static void dem_layer_file_info ( GtkWidget *widget, struct LatLon *ll )
1199 {
1200   gint intlat, intlon;
1201   const gchar *continent_dir;
1202
1203   intlat = (int)floor(ll->lat);
1204   intlon = (int)floor(ll->lon);
1205   continent_dir = srtm_continent_dir(intlat, intlon);
1206
1207   gchar *source = NULL;
1208   if ( continent_dir )
1209     source = g_strdup_printf ( "http://%s%s%s/%c%02d%c%03d.hgt.zip",
1210                                SRTM_HTTP_SITE,
1211                                SRTM_HTTP_URI,
1212                                continent_dir,
1213                                (intlat >= 0) ? 'N' : 'S',
1214                                ABS(intlat),
1215                                (intlon >= 0) ? 'E' : 'W',
1216                                ABS(intlon) );
1217   else
1218     // Probably not over any land...
1219     source = g_strdup ( _("No DEM File Available") );
1220
1221   gchar *filename = NULL;
1222   gchar *dem_file = NULL;
1223 #ifdef VIK_CONFIG_DEM24K
1224   dem_file = dem24k_lat_lon_to_dest_fn ( ll->lat, ll->lon );
1225 #else
1226   dem_file = srtm_lat_lon_to_dest_fn ( ll->lat, ll->lon );
1227 #endif
1228   gchar *message = NULL;
1229
1230   filename = g_strdup_printf ( "%s%s", MAPS_CACHE_DIR, dem_file );
1231
1232   if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1233     // Get some timestamp information of the file
1234     struct stat stat_buf;
1235     if ( g_stat ( filename, &stat_buf ) == 0 ) {
1236       gchar time_buf[64];
1237       strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
1238       message = g_strdup_printf ( _("\nSource: %s\n\nDEM File: %s\nDEM File Timestamp: %s"), source, filename, time_buf );
1239     }
1240   }
1241   else
1242     message = g_strdup_printf ( _("Source: %s\n\nNo DEM File!"), source );
1243
1244   // Show the info
1245   a_dialog_info_msg ( GTK_WINDOW(gtk_widget_get_toplevel(widget)), message );
1246
1247   g_free ( message );
1248   g_free ( source );
1249   g_free ( dem_file );
1250   g_free ( filename );
1251 }
1252
1253 static gboolean dem_layer_download_release ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1254 {
1255   VikCoord coord;
1256   static struct LatLon ll;
1257
1258   gchar *full_path;
1259   gchar *dem_file = NULL;
1260
1261   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1262   vik_coord_to_latlon ( &coord, &ll );
1263
1264   
1265   if ( vdl->source == DEM_SOURCE_SRTM )
1266     dem_file = srtm_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1267 #ifdef VIK_CONFIG_DEM24K
1268   else if ( vdl->source == DEM_SOURCE_DEM24K )
1269     dem_file = dem24k_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1270 #endif
1271
1272   if ( ! dem_file )
1273     return TRUE;
1274
1275   full_path = g_strdup_printf("%s%s", MAPS_CACHE_DIR, dem_file );
1276
1277   g_debug("%s: %s", __FUNCTION__, full_path);
1278
1279   if ( event->button == 1 ) {
1280     // TODO: check if already in filelist
1281     if ( ! dem_layer_add_file(vdl, full_path) ) {
1282       gchar *tmp = g_strdup_printf ( _("Downloading DEM %s"), dem_file );
1283       DEMDownloadParams *p = g_malloc(sizeof(DEMDownloadParams));
1284       p->dest = g_strdup(full_path);
1285       p->lat = ll.lat;
1286       p->lon = ll.lon;
1287       p->vdl = vdl;
1288       p->mutex = vik_mutex_new();
1289       p->source = vdl->source;
1290       g_object_weak_ref(G_OBJECT(p->vdl), weak_ref_cb, p );
1291
1292       a_background_thread ( BACKGROUND_POOL_REMOTE,
1293                             VIK_GTK_WINDOW_FROM_LAYER(vdl), tmp,
1294                             (vik_thr_func) dem_download_thread, p,
1295                             (vik_thr_free_func) free_dem_download_params, NULL, 1 );
1296
1297       g_free ( tmp );
1298     }
1299     else
1300       vik_layer_emit_update ( VIK_LAYER(vdl) );
1301   }
1302   else {
1303     if ( !vdl->right_click_menu ) {
1304       GtkWidget *item;
1305       vdl->right_click_menu = GTK_MENU ( gtk_menu_new () );
1306
1307       item = gtk_image_menu_item_new_with_mnemonic ( _("_Show DEM File Information") );
1308       gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
1309       g_signal_connect ( G_OBJECT(item), "activate", G_CALLBACK(dem_layer_file_info), &ll );
1310       gtk_menu_shell_append (GTK_MENU_SHELL(vdl->right_click_menu), item);
1311     }
1312
1313     gtk_menu_popup ( vdl->right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1314     gtk_widget_show_all ( GTK_WIDGET(vdl->right_click_menu) );
1315   }
1316
1317   g_free ( dem_file );
1318   g_free ( full_path );
1319
1320   return TRUE;
1321 }
1322
1323 static gboolean dem_layer_download_click ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1324 {
1325 /* choose & keep track of cache dir
1326  * download in background thread
1327  * download over area */
1328   return TRUE;
1329 }
1330
1331