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