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