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