]> git.street.me.uk Git - andy/viking.git/blob - src/vikdemlayer.c
Add internal track right click menu variable.
[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/gi18n.h>
39
40 #include "config.h"
41 #include "globals.h"
42 #include "coords.h"
43 #include "vikcoord.h"
44 #include "download.h"
45 #include "background.h"
46 #include "vikwaypoint.h"
47 #include "viktrack.h"
48 #include "vikviewport.h"
49 #include "viktreeview.h"
50 #include "viklayer.h"
51 #include "vikaggregatelayer.h"
52 #include "viklayerspanel.h"
53 #include "vikmapslayer.h"
54 #include "vikdemlayer.h"
55 #include "dialog.h"
56
57 #include "dem.h"
58 #include "dems.h"
59
60 #include "icons/icons.h"
61
62 #define MAPS_CACHE_DIR maps_layer_default_dir()
63
64 #define SRTM_CACHE_TEMPLATE "%ssrtm3-%s%s%c%02d%c%03d.hgt.zip"
65 #define SRTM_HTTP_SITE "dds.cr.usgs.gov"
66 #define SRTM_HTTP_URI  "/srtm/version2_1/SRTM3/"
67
68 #ifdef VIK_CONFIG_DEM24K
69 #define DEM24K_DOWNLOAD_SCRIPT "dem24k.pl"
70 #endif
71
72 #define UNUSED_LINE_THICKNESS 3
73
74 static const gchar* dem_layer_tooltip( VikDEMLayer *vdl );
75 static void dem_layer_marshall( VikDEMLayer *vdl, guint8 **data, gint *len );
76 static VikDEMLayer *dem_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
77 static gboolean dem_layer_set_param ( VikDEMLayer *vdl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
78 static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gboolean is_file_operation );
79 static void dem_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file );
80 static void srtm_draw_existence ( VikViewport *vp );
81
82 #ifdef VIK_CONFIG_DEM24K
83 static void dem24k_draw_existence ( VikViewport *vp );
84 #endif
85
86 /* Upped upper limit incase units are feet */
87 static VikLayerParamScale param_scales[] = {
88   { 0, 30000, 10, 1 },
89   { 1, 30000, 10, 1 },
90 };
91
92 static gchar *params_source[] = {
93         "SRTM Global 90m (3 arcsec)",
94 #ifdef VIK_CONFIG_DEM24K
95         "USA 10m (USGS 24k)",
96 #endif
97         "None",
98         NULL
99         };
100
101 static gchar *params_type[] = {
102         N_("Absolute height"),
103         N_("Height gradient"),
104         NULL
105 };
106
107 enum { DEM_SOURCE_SRTM,
108 #ifdef VIK_CONFIG_DEM24K
109        DEM_SOURCE_DEM24K,
110 #endif
111        DEM_SOURCE_NONE,
112      };
113
114 enum { DEM_TYPE_HEIGHT = 0,
115        DEM_TYPE_GRADIENT,
116        DEM_TYPE_NONE,
117 };
118
119 static VikLayerParam dem_layer_params[] = {
120   { "files", VIK_LAYER_PARAM_STRING_LIST, VIK_LAYER_GROUP_NONE, N_("DEM Files:"), VIK_LAYER_WIDGET_FILELIST },
121   { "source", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Download Source:"), VIK_LAYER_WIDGET_RADIOGROUP_STATIC, params_source, NULL },
122   { "color", VIK_LAYER_PARAM_COLOR, VIK_LAYER_GROUP_NONE, N_("Min Elev Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
123   { "type", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Type:"), VIK_LAYER_WIDGET_RADIOGROUP_STATIC, params_type, NULL },
124   { "min_elev", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_GROUP_NONE, N_("Min Elev:"), VIK_LAYER_WIDGET_SPINBUTTON, param_scales + 0 },
125   { "max_elev", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_GROUP_NONE, N_("Max Elev:"), VIK_LAYER_WIDGET_SPINBUTTON, param_scales + 0 },
126 };
127
128
129 enum { PARAM_FILES=0, PARAM_SOURCE, PARAM_COLOR, PARAM_TYPE, PARAM_MIN_ELEV, PARAM_MAX_ELEV, NUM_PARAMS };
130
131 static gpointer dem_layer_download_create ( VikWindow *vw, VikViewport *vvp);
132 static gboolean dem_layer_download_release ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp );
133 static gboolean dem_layer_download_click ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp );
134
135 static VikToolInterface dem_tools[] = {
136   { N_("DEM Download/Import"), (VikToolConstructorFunc) dem_layer_download_create, NULL, NULL, NULL,
137     (VikToolMouseFunc) dem_layer_download_click, NULL,  (VikToolMouseFunc) dem_layer_download_release,
138     (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_demdl_pixbuf },
139 };
140
141
142 /* HEIGHT COLORS
143    The first entry is blue for a default 'sea' colour,
144    however the value used by the corresponding gc can be configured as part of the DEM layer properties.
145    The other colours, shaded from brown to white are used to give an indication of height.
146 */
147 static gchar *dem_height_colors[] = {
148 "#0000FF",
149 "#9b793c", "#9c7d40", "#9d8144", "#9e8549", "#9f894d", "#a08d51", "#a29156", "#a3955a", "#a4995e", "#a69d63",
150 "#a89f65", "#aaa267", "#ada569", "#afa76b", "#b1aa6d", "#b4ad6f", "#b6b071", "#b9b373", "#bcb676", "#beb978",
151 "#c0bc7a", "#c2c07d", "#c4c37f", "#c6c681", "#c8ca84", "#cacd86", "#ccd188", "#cfd58b", "#c2ce84", "#b5c87e",
152 "#a9c278", "#9cbb71", "#8fb56b", "#83af65", "#76a95e", "#6aa358", "#5e9d52", "#63a055", "#69a458", "#6fa85c",
153 "#74ac5f", "#7ab063", "#80b467", "#86b86a", "#8cbc6e", "#92c072", "#94c175", "#97c278", "#9ac47c", "#9cc57f",
154 "#9fc682", "#a2c886", "#a4c989", "#a7cb8d", "#aacd91", "#afce99", "#b5d0a1", "#bbd2aa", "#c0d3b2", "#c6d5ba",
155 "#ccd7c3", "#d1d9cb", "#d7dbd4", "#DDDDDD", "#e0e0e0", "#e4e4e4", "#e8e8e8", "#ebebeb", "#efefef", "#f3f3f3",
156 "#f7f7f7", "#fbfbfb", "#ffffff"
157 };
158
159 static const guint DEM_N_HEIGHT_COLORS = sizeof(dem_height_colors)/sizeof(dem_height_colors[0]);
160
161 /*
162 "#9b793c", "#9e8549", "#a29156", "#a69d63", "#ada569", "#b4ad6f", "#bcb676", "#c2c07d", "#c8ca84", "#cfd58b",
163 "#a9c278", "#83af65", "#5e9d52", "#6fa85c", "#80b467", "#92c072", "#9ac47c", "#a2c886", "#aacd91", "#bbd2aa",
164 "#ccd7c3", "#DDDDDD", "#e8e8e8", "#f3f3f3", "#FFFFFF"
165 };
166 */
167
168 static gchar *dem_gradient_colors[] = {
169 "#AAAAAA",
170 "#000000", "#000011", "#000022", "#000033", "#000044", "#00004c", "#000055", "#00005d", "#000066", "#00006e",
171 "#000077", "#00007f", "#000088", "#000090", "#000099", "#0000a1", "#0000aa", "#0000b2", "#0000bb", "#0000c3",
172 "#0000cc", "#0000d4", "#0000dd", "#0000e5", "#0000ee", "#0000f6", "#0000ff", "#0008f7", "#0011ee", "#0019e6",
173 "#0022dd", "#002ad5", "#0033cc", "#003bc4", "#0044bb", "#004cb3", "#0055aa", "#005da2", "#006699", "#006e91",
174 "#007788", "#007f80", "#008877", "#00906f", "#009966", "#00a15e", "#00aa55", "#00b24d", "#00bb44", "#00c33c",
175 "#00cc33", "#00d42b", "#00dd22", "#00e51a", "#00ee11", "#00f609", "#00ff00", "#08f700", "#11ee00", "#19e600",
176 "#22dd00", "#2ad500", "#33cc00", "#3bc400", "#44bb00", "#4cb300", "#55aa00", "#5da200", "#669900", "#6e9100",
177 "#778800", "#7f8000", "#887700", "#906f00", "#996600", "#a15e00", "#aa5500", "#b24d00", "#bb4400", "#c33c00",
178 "#cc3300", "#d42b00", "#dd2200", "#e51a00", "#ee1100", "#f60900", "#ff0000",
179 "#FFFFFF"
180 };
181
182 static const guint DEM_N_GRADIENT_COLORS = sizeof(dem_gradient_colors)/sizeof(dem_gradient_colors[0]);
183
184
185 VikLayerInterface vik_dem_layer_interface = {
186   "DEM",
187   &vikdemlayer_pixbuf,
188
189   dem_tools,
190   sizeof(dem_tools) / sizeof(dem_tools[0]),
191
192   dem_layer_params,
193   NUM_PARAMS,
194   NULL,
195   0,
196
197   VIK_MENU_ITEM_ALL,
198
199   (VikLayerFuncCreate)                  vik_dem_layer_create,
200   (VikLayerFuncRealize)                 NULL,
201   (VikLayerFuncPostRead)                dem_layer_post_read,
202   (VikLayerFuncFree)                    vik_dem_layer_free,
203
204   (VikLayerFuncProperties)              NULL,
205   (VikLayerFuncDraw)                    vik_dem_layer_draw,
206   (VikLayerFuncChangeCoordMode)         NULL,
207
208   (VikLayerFuncSetMenuItemsSelection)   NULL,
209   (VikLayerFuncGetMenuItemsSelection)   NULL,
210
211   (VikLayerFuncAddMenuItems)            NULL,
212   (VikLayerFuncSublayerAddMenuItems)    NULL,
213
214   (VikLayerFuncSublayerRenameRequest)   NULL,
215   (VikLayerFuncSublayerToggleVisible)   NULL,
216   (VikLayerFuncSublayerTooltip)         NULL,
217   (VikLayerFuncLayerTooltip)            dem_layer_tooltip,
218   (VikLayerFuncLayerSelected)           NULL,
219
220   (VikLayerFuncMarshall)                dem_layer_marshall,
221   (VikLayerFuncUnmarshall)              dem_layer_unmarshall,
222
223   (VikLayerFuncSetParam)                dem_layer_set_param,
224   (VikLayerFuncGetParam)                dem_layer_get_param,
225
226   (VikLayerFuncReadFileData)            NULL,
227   (VikLayerFuncWriteFileData)           NULL,
228
229   (VikLayerFuncDeleteItem)              NULL,
230   (VikLayerFuncCutItem)                 NULL,
231   (VikLayerFuncCopyItem)                NULL,
232   (VikLayerFuncPasteItem)               NULL,
233   (VikLayerFuncFreeCopiedItem)          NULL,
234   (VikLayerFuncDragDropRequest)         NULL,
235 };
236
237 struct _VikDEMLayer {
238   VikLayer vl;
239   GdkGC **gcs;
240   GdkGC **gcsgradient;
241   GList *files;
242   gdouble min_elev;
243   gdouble max_elev;
244   GdkGC *color;
245   guint source;
246   guint type;
247 };
248
249 GType vik_dem_layer_get_type ()
250 {
251   static GType vdl_type = 0;
252
253   if (!vdl_type)
254   {
255     static const GTypeInfo vdl_info =
256     {
257       sizeof (VikDEMLayerClass),
258       NULL, /* base_init */
259       NULL, /* base_finalize */
260       NULL, /* class init */
261       NULL, /* class_finalize */
262       NULL, /* class_data */
263       sizeof (VikDEMLayer),
264       0,
265       NULL /* instance init */
266     };
267     vdl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikDEMLayer", &vdl_info, 0 );
268   }
269
270   return vdl_type;
271 }
272
273 static const gchar* dem_layer_tooltip( VikDEMLayer *vdl )
274 {
275   static gchar tmp_buf[100];
276   g_snprintf (tmp_buf, sizeof(tmp_buf), _("Number of files: %d"), g_list_length (vdl->files));
277   return tmp_buf;
278 }
279
280 static void dem_layer_marshall( VikDEMLayer *vdl, guint8 **data, gint *len )
281 {
282   vik_layer_marshall_params ( VIK_LAYER(vdl), data, len );
283 }
284
285 static VikDEMLayer *dem_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
286 {
287   VikDEMLayer *rv = vik_dem_layer_new ();
288   gint i;
289
290   /* TODO: share GCS between layers */
291   for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
292     rv->gcs[i] = vik_viewport_new_gc ( vvp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
293
294   for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
295     rv->gcsgradient[i] = vik_viewport_new_gc ( vvp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
296
297   vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
298   return rv;
299 }
300
301 /* Structure for DEM data used in background thread */
302 typedef struct {
303   VikDEMLayer *vdl;
304 } dem_load_thread_data;
305
306 /*
307  * Function for starting the DEM file loading as a background thread
308  */
309 static int dem_layer_load_list_thread ( dem_load_thread_data *dltd, gpointer threaddata )
310 {
311   int result = 0; // Default to good
312   // Actual Load
313   if ( a_dems_load_list ( &(dltd->vdl->files), threaddata ) ) {
314     // Thread cancelled
315     result = -1;
316   }
317
318   // ATM as each file is processed the screen is not updated (no mechanism exposed to a_dems_load_list)
319   // Thus force draw only at the end, as loading is complete/aborted
320   gdk_threads_enter();
321   // Test is helpful to prevent Gtk-CRITICAL warnings if the program is exitted whilst loading
322   if ( IS_VIK_LAYER(dltd->vdl) )
323     vik_layer_emit_update ( VIK_LAYER(dltd->vdl) );
324   gdk_threads_leave();
325
326   return result;
327 }
328
329 static void dem_layer_thread_data_free ( dem_load_thread_data *data )
330 {
331   // Simple release
332   g_free ( data );
333 }
334
335 static void dem_layer_thread_cancel ( dem_load_thread_data *data )
336 {
337   // Abort loading
338   // Instead of freeing the list, leave it as partially processed
339   // Thus we can see/use what was done
340 }
341
342 gboolean dem_layer_set_param ( VikDEMLayer *vdl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
343 {
344   switch ( id )
345   {
346     case PARAM_COLOR: gdk_gc_set_rgb_fg_color ( vdl->gcs[0], &(data.c) ); break;
347     case PARAM_SOURCE: vdl->source = data.u; break;
348     case PARAM_TYPE: vdl->type = data.u; break;
349     case PARAM_MIN_ELEV:
350       /* Convert to store internally
351          NB file operation always in internal units (metres) */
352       if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
353         vdl->min_elev = VIK_FEET_TO_METERS(data.d);
354       else
355         vdl->min_elev = data.d;
356       break;
357     case PARAM_MAX_ELEV:
358       /* Convert to store internally
359          NB file operation always in internal units (metres) */
360       if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
361         vdl->max_elev = VIK_FEET_TO_METERS(data.d);
362       else
363         vdl->max_elev = data.d;
364       break;
365     case PARAM_FILES:
366     {
367       // Clear out old settings - if any commonalities with new settings they will have to be read again
368       a_dems_list_free ( vdl->files );
369       // Set file list so any other intermediate screen drawing updates will show currently loaded DEMs by the working thread
370       vdl->files = data.sl;
371       // Thread Load
372       dem_load_thread_data *dltd = g_malloc ( sizeof(dem_load_thread_data) );
373       dltd->vdl = vdl;
374       dltd->vdl->files = data.sl;
375
376       a_background_thread ( VIK_GTK_WINDOW_FROM_WIDGET(vp),
377                             _("DEM Loading"),
378                             (vik_thr_func) dem_layer_load_list_thread,
379                             dltd,
380                             (vik_thr_free_func) dem_layer_thread_data_free,
381                             (vik_thr_free_func) dem_layer_thread_cancel,
382                             g_list_length ( data.sl ) ); // Number of DEM files
383       break;
384     }
385   }
386   return TRUE;
387 }
388
389 static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gboolean is_file_operation )
390 {
391   VikLayerParamData rv;
392   switch ( id )
393   {
394     case PARAM_FILES: rv.sl = vdl->files; break;
395     case PARAM_SOURCE: rv.u = vdl->source; break;
396     case PARAM_TYPE: rv.u = vdl->type; break;
397     case PARAM_COLOR: vik_gc_get_fg_color ( vdl->gcs[0], &(rv.c) ); break;
398     case PARAM_MIN_ELEV:
399       /* Convert for display in desired units
400          NB file operation always in internal units (metres) */
401       if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
402         rv.d = VIK_METERS_TO_FEET(vdl->min_elev);
403       else
404         rv.d = vdl->min_elev;
405       break;
406     case PARAM_MAX_ELEV:
407       /* Convert for display in desired units
408          NB file operation always in internal units (metres) */
409       if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
410         rv.d = VIK_METERS_TO_FEET(vdl->max_elev);
411       else
412         rv.d = vdl->max_elev;
413       break;
414   }
415   return rv;
416 }
417
418 static void dem_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file )
419 {
420   /* nothing ATM, but keep in case it's needed the future */
421 }
422
423 VikDEMLayer *vik_dem_layer_new ( )
424 {
425   VikDEMLayer *vdl = VIK_DEM_LAYER ( g_object_new ( VIK_DEM_LAYER_TYPE, NULL ) );
426
427   vik_layer_init ( VIK_LAYER(vdl), VIK_LAYER_DEM );
428
429   vdl->files = NULL;
430
431   vdl->gcs = g_malloc(sizeof(GdkGC *)*DEM_N_HEIGHT_COLORS);
432   vdl->gcsgradient = g_malloc(sizeof(GdkGC *)*DEM_N_GRADIENT_COLORS);
433   /* make new gcs only if we need it (copy layer -> use old) */
434
435   vdl->min_elev = 0.0;
436   vdl->max_elev = 1000.0;
437   vdl->source = DEM_SOURCE_SRTM;
438   vdl->type = DEM_TYPE_HEIGHT;
439   return vdl;
440 }
441
442
443 static inline guint16 get_height_difference(gint16 elev, gint16 new_elev)
444 {
445   if(new_elev == VIK_DEM_INVALID_ELEVATION)
446     return 0;
447   else
448     return abs(new_elev - elev);
449 }
450
451
452 static void vik_dem_layer_draw_dem ( VikDEMLayer *vdl, VikViewport *vp, VikDEM *dem )
453 {
454   VikDEMColumn *column, *prevcolumn, *nextcolumn;
455
456   struct LatLon dem_northeast, dem_southwest;
457   gdouble max_lat, max_lon, min_lat, min_lon;
458
459   /**** Check if viewport and DEM data overlap ****/
460
461   /* get min, max lat/lon of viewport */
462   vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
463
464   /* get min, max lat/lon of DEM data */
465   if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
466     dem_northeast.lat = dem->max_north / 3600.0;
467     dem_northeast.lon = dem->max_east / 3600.0;
468     dem_southwest.lat = dem->min_north / 3600.0;
469     dem_southwest.lon = dem->min_east / 3600.0;
470   } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
471     struct UTM dem_northeast_utm, dem_southwest_utm;
472     dem_northeast_utm.northing = dem->max_north;
473     dem_northeast_utm.easting = dem->max_east;
474     dem_southwest_utm.northing = dem->min_north;
475     dem_southwest_utm.easting = dem->min_east;
476     dem_northeast_utm.zone = dem_southwest_utm.zone = dem->utm_zone;
477     dem_northeast_utm.letter = dem_southwest_utm.letter = dem->utm_letter;
478
479     a_coords_utm_to_latlon(&dem_northeast_utm, &dem_northeast);
480     a_coords_utm_to_latlon(&dem_southwest_utm, &dem_southwest);
481   }
482
483   if ( (max_lat > dem_northeast.lat && min_lat > dem_northeast.lat) ||
484        (max_lat < dem_southwest.lat && min_lat < dem_southwest.lat) )
485     return;
486   else if ( (max_lon > dem_northeast.lon && min_lon > dem_northeast.lon) ||
487             (max_lon < dem_southwest.lon && min_lon < dem_southwest.lon) )
488     return;
489   /* else they overlap */
490
491   /**** End Overlap Check ****/
492   /* boxes to show where we have DEM instead of actually drawing the DEM.
493    * useful if we want to see what areas we have coverage for (if we want
494    * to get elevation data for a track) but don't want to cover the map.
495    */
496
497   #if 0
498   /* draw a box if a DEM is loaded. in future I'd like to add an option for this
499    * this is useful if we want to see what areas we have dem for but don't want to
500    * cover the map (or maybe we just need translucent DEM?) */
501   {
502     VikCoord demne, demsw;
503     gint x1, y1, x2, y2;
504     vik_coord_load_from_latlon(&demne, vik_viewport_get_coord_mode(vp), &dem_northeast);
505     vik_coord_load_from_latlon(&demsw, vik_viewport_get_coord_mode(vp), &dem_southwest);
506
507     vik_viewport_coord_to_screen ( vp, &demne, &x1, &y1 );
508     vik_viewport_coord_to_screen ( vp, &demsw, &x2, &y2 );
509
510     if ( x1 > vik_viewport_get_width(vp) ) x1=vik_viewport_get_width(vp);
511     if ( y2 > vik_viewport_get_height(vp) ) y2=vik_viewport_get_height(vp);
512     if ( x2 < 0 ) x2 = 0;
513     if ( y1 < 0 ) y1 = 0;
514     vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc, 
515         FALSE, x2, y1, x1-x2, y2-y1 );
516     return;
517   }
518   #endif
519
520   if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
521     VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
522
523     gdouble max_lat_as, max_lon_as, min_lat_as, min_lon_as;  
524     gdouble start_lat_as, end_lat_as, start_lon_as, end_lon_as;
525
526     gdouble start_lat, end_lat, start_lon, end_lon;
527
528     struct LatLon counter;
529
530     guint x, y, start_x, start_y;
531
532     gint16 elev;
533
534     guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 80 ); /* todo: smarter calculation. */
535
536     gdouble nscale_deg = dem->north_scale / ((gdouble) 3600);
537     gdouble escale_deg = dem->east_scale / ((gdouble) 3600);
538
539     max_lat_as = max_lat * 3600;
540     min_lat_as = min_lat * 3600;
541     max_lon_as = max_lon * 3600;
542     min_lon_as = min_lon * 3600;
543
544     start_lat_as = MAX(min_lat_as, dem->min_north);
545     end_lat_as   = MIN(max_lat_as, dem->max_north);
546     start_lon_as = MAX(min_lon_as, dem->min_east);
547     end_lon_as   = MIN(max_lon_as, dem->max_east);
548
549     start_lat = floor(start_lat_as / dem->north_scale) * nscale_deg;
550     end_lat   = ceil (end_lat_as / dem->north_scale) * nscale_deg;
551     start_lon = floor(start_lon_as / dem->east_scale) * escale_deg;
552     end_lon   = ceil (end_lon_as / dem->east_scale) * escale_deg;
553
554     vik_dem_east_north_to_xy ( dem, start_lon_as, start_lat_as, &start_x, &start_y );
555     guint gradient_skip_factor = 1;
556     if(vdl->type == DEM_TYPE_GRADIENT)
557             gradient_skip_factor = skip_factor;
558
559     /* verify sane elev interval */
560     if ( vdl->max_elev <= vdl->min_elev )
561       vdl->max_elev = vdl->min_elev + 1;
562
563     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 ) {
564       // NOTE: ( counter.lon <= end_lon + ESCALE_DEG*SKIP_FACTOR ) is neccessary so in high zoom modes,
565       // the leftmost column does also get drawn, if the center point is out of viewport.
566       if ( x >= 0 && x < dem->n_columns ) {
567         column = g_ptr_array_index ( dem->columns, x );
568         // get previous and next column. catch out-of-bound.
569         gint32 new_x = x;
570         new_x -= gradient_skip_factor;
571         if(new_x < 1)
572           prevcolumn = g_ptr_array_index ( dem->columns, x+1);
573         else
574           prevcolumn = g_ptr_array_index ( dem->columns, new_x);
575         new_x = x;
576         new_x += gradient_skip_factor;
577         if(new_x >= dem->n_columns)
578           nextcolumn = g_ptr_array_index ( dem->columns, x-1);
579         else
580           nextcolumn = g_ptr_array_index ( dem->columns, new_x);
581
582         for ( y=start_y, counter.lat = start_lat; counter.lat <= end_lat; counter.lat += nscale_deg * skip_factor, y += skip_factor ) {
583           if ( y > column->n_points )
584             break;
585
586           elev = column->points[y];
587
588           // calculate bounding box for drawing
589           gint box_x, box_y, box_width, box_height;
590           struct LatLon box_c;
591           box_c = counter;
592           box_c.lat += (nscale_deg * skip_factor)/2;
593           box_c.lon -= (escale_deg * skip_factor)/2;
594           vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
595           vik_viewport_coord_to_screen(vp, &tmp, &box_x, &box_y);
596           // catch box at borders
597           if(box_x < 0)
598                   box_x = 0;
599           if(box_y < 0)
600                   box_y = 0;
601           box_c.lat -= nscale_deg * skip_factor;
602           box_c.lon += escale_deg * skip_factor;
603           vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
604           vik_viewport_coord_to_screen(vp, &tmp, &box_width, &box_height);
605           box_width -= box_x;
606           box_height -= box_y;
607           // catch box at borders
608           if(box_width < 0 || box_height < 0)
609                   continue; // skip this. this is out of our viewport anyway. FIXME: why?
610
611           gboolean below_minimum = FALSE;
612           if(vdl->type == DEM_TYPE_HEIGHT) {
613             if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev ) {
614               // Prevent 'elev - vdl->min_elev' from being negative so can safely use as array index
615               elev = ceil ( vdl->min_elev );
616               below_minimum = TRUE;
617             }
618             if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
619               elev = vdl->max_elev;
620           }
621
622           {
623             if(box_width < 0 || box_height < 0) // FIXME: why does this happen?
624               continue;
625
626             if(vdl->type == DEM_TYPE_GRADIENT) {
627               if( elev == VIK_DEM_INVALID_ELEVATION ) {
628                 /* don't draw it */
629               } else {
630                 // calculate and sum gradient in all directions
631                 gint16 change = 0;
632                 gint32 new_y;
633
634                 // calculate gradient from height points all around the current one
635                 new_y = y - gradient_skip_factor;
636                 if(new_y < 0)
637                         new_y = y;
638                 change += get_height_difference(elev, prevcolumn->points[new_y]);
639                 change += get_height_difference(elev, column->points[new_y]);
640                 change += get_height_difference(elev, nextcolumn->points[new_y]);
641
642                 change += get_height_difference(elev, prevcolumn->points[y]);
643                 change += get_height_difference(elev, nextcolumn->points[y]);
644
645                 new_y = y + gradient_skip_factor;
646                 if(new_y >= column->n_points)
647                         new_y = y;
648                 change += get_height_difference(elev, prevcolumn->points[new_y]);
649                 change += get_height_difference(elev, column->points[new_y]);
650                 change += get_height_difference(elev, nextcolumn->points[new_y]);
651
652                 change = change / ((skip_factor > 1) ? log(skip_factor) : 0.55); // FIXME: better calc.
653
654                 if(change < vdl->min_elev)
655                   // Prevent 'change - vdl->min_elev' from being negative so can safely use as array index
656                   change = ceil ( vdl->min_elev );
657
658                 if(change > vdl->max_elev)
659                   change = vdl->max_elev;
660
661                 // void vik_viewport_draw_rectangle ( VikViewport *vvp, GdkGC *gc, gboolean filled, gint x1, gint y1, gint x2, gint y2 );
662                 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);
663               }
664             } else {
665               if(vdl->type == DEM_TYPE_HEIGHT) {
666                 if ( elev == VIK_DEM_INVALID_ELEVATION )
667                   ; /* don't draw it */
668                 else if ( elev <= 0 || below_minimum )
669                   /* If 'sea' colour or below the defined mininum draw in the configurable colour */
670                   vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, box_x, box_y, box_width, box_height);
671                 else
672                   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);
673               }
674             }
675           }
676         } /* for y= */
677       }
678     } /* for x= */
679   } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
680     gdouble max_nor, max_eas, min_nor, min_eas;
681     gdouble start_nor, start_eas, end_nor, end_eas;
682
683     gint16 elev;
684
685     guint x, y, start_x, start_y;
686
687     VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
688     struct UTM counter;
689
690     guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 10 ); /* todo: smarter calculation. */
691
692     VikCoord tleft, tright, bleft, bright;
693
694     vik_viewport_screen_to_coord ( vp, 0, 0, &tleft );
695     vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), 0, &tright );
696     vik_viewport_screen_to_coord ( vp, 0, vik_viewport_get_height(vp), &bleft );
697     vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), vik_viewport_get_height(vp), &bright );
698
699
700     vik_coord_convert(&tleft, VIK_COORD_UTM);
701     vik_coord_convert(&tright, VIK_COORD_UTM);
702     vik_coord_convert(&bleft, VIK_COORD_UTM);
703     vik_coord_convert(&bright, VIK_COORD_UTM);
704
705     max_nor = MAX(tleft.north_south, tright.north_south);
706     min_nor = MIN(bleft.north_south, bright.north_south);
707     max_eas = MAX(bright.east_west, tright.east_west);
708     min_eas = MIN(bleft.east_west, tleft.east_west);
709
710     start_nor = MAX(min_nor, dem->min_north);
711     end_nor   = MIN(max_nor, dem->max_north);
712     if ( tleft.utm_zone == dem->utm_zone && bleft.utm_zone == dem->utm_zone
713          && (tleft.utm_letter >= 'N') == (dem->utm_letter >= 'N')
714          && (bleft.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
715       start_eas = MAX(min_eas, dem->min_east);
716     else
717       start_eas = dem->min_east;
718     if ( tright.utm_zone == dem->utm_zone && bright.utm_zone == dem->utm_zone
719          && (tright.utm_letter >= 'N') == (dem->utm_letter >= 'N')
720          && (bright.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
721       end_eas = MIN(max_eas, dem->max_east);
722     else
723       end_eas = dem->max_east;
724
725     start_nor = floor(start_nor / dem->north_scale) * dem->north_scale;
726     end_nor   = ceil (end_nor / dem->north_scale) * dem->north_scale;
727     start_eas = floor(start_eas / dem->east_scale) * dem->east_scale;
728     end_eas   = ceil (end_eas / dem->east_scale) * dem->east_scale;
729
730     vik_dem_east_north_to_xy ( dem, start_eas, start_nor, &start_x, &start_y );
731
732     /* TODO: why start_x and start_y are -1 -- rounding error from above? */
733
734     counter.zone = dem->utm_zone;
735     counter.letter = dem->utm_letter;
736
737     for ( x=start_x, counter.easting = start_eas; counter.easting <= end_eas; counter.easting += dem->east_scale * skip_factor, x += skip_factor ) {
738       if ( x > 0 && x < dem->n_columns ) {
739         column = g_ptr_array_index ( dem->columns, x );
740         for ( y=start_y, counter.northing = start_nor; counter.northing <= end_nor; counter.northing += dem->north_scale * skip_factor, y += skip_factor ) {
741           if ( y > column->n_points )
742             continue;
743           elev = column->points[y];
744           if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev )
745             elev=vdl->min_elev;
746           if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
747             elev=vdl->max_elev;
748
749           {
750             gint a, b;
751             vik_coord_load_from_utm(&tmp, vik_viewport_get_coord_mode(vp), &counter);
752                     vik_viewport_coord_to_screen(vp, &tmp, &a, &b);
753             if ( elev == VIK_DEM_INVALID_ELEVATION )
754               ; /* don't draw it */
755             else if ( elev <= 0 )
756               vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, a-1, b-1, 2, 2 );
757             else
758               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 );
759           }
760         } /* for y= */
761       }
762     } /* for x= */
763   }
764 }
765
766 /* return the continent for the specified lat, lon */
767 /* TODO */
768 static const gchar *srtm_continent_dir ( gint lat, gint lon )
769 {
770   extern const char *_srtm_continent_data[];
771   static GHashTable *srtm_continent = NULL;
772   const gchar *continent;
773   gchar name[16];
774
775   if (!srtm_continent) {
776     const gchar **s;
777
778     srtm_continent = g_hash_table_new(g_str_hash, g_str_equal);
779     s = _srtm_continent_data;
780     while (*s != (gchar *)-1) {
781       continent = *s++;
782       while (*s) {
783         g_hash_table_insert(srtm_continent, (gpointer) *s, (gpointer) continent);
784         s++;
785       }
786       s++;
787     }
788   }
789   g_snprintf(name, sizeof(name), "%c%02d%c%03d",
790                   (lat >= 0) ? 'N' : 'S', ABS(lat),
791                   (lon >= 0) ? 'E' : 'W', ABS(lon));
792
793   return(g_hash_table_lookup(srtm_continent, name));
794 }
795
796 void vik_dem_layer_draw ( VikDEMLayer *vdl, gpointer data )
797 {
798   VikViewport *vp = (VikViewport *) data;
799   GList *dems_iter = vdl->files;
800   VikDEM *dem;
801
802
803   /* search for SRTM3 90m */
804
805   if ( vdl->source == DEM_SOURCE_SRTM )
806     srtm_draw_existence ( vp );
807 #ifdef VIK_CONFIG_DEM24K
808   else if ( vdl->source == DEM_SOURCE_DEM24K )
809     dem24k_draw_existence ( vp );
810 #endif
811
812   while ( dems_iter ) {
813     dem = a_dems_get ( (const char *) (dems_iter->data) );
814     if ( dem )
815       vik_dem_layer_draw_dem ( vdl, vp, dem );
816     dems_iter = dems_iter->next;
817   }
818 }
819
820 void vik_dem_layer_free ( VikDEMLayer *vdl )
821 {
822   gint i;
823   if ( vdl->color != NULL )
824     g_object_unref ( vdl->color );
825
826   if ( vdl->gcs )
827     for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
828       g_object_unref ( vdl->gcs[i] );
829   g_free ( vdl->gcs );
830
831   if ( vdl->gcsgradient )
832     for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
833       g_object_unref ( vdl->gcsgradient[i] );
834   g_free ( vdl->gcsgradient );
835
836   a_dems_list_free ( vdl->files );
837 }
838
839 VikDEMLayer *vik_dem_layer_create ( VikViewport *vp )
840 {
841   VikDEMLayer *vdl = vik_dem_layer_new ();
842   gint i;
843
844   /* TODO: share GCS between layers */
845   for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
846     vdl->gcs[i] = vik_viewport_new_gc ( vp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
847
848   for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
849     vdl->gcsgradient[i] = vik_viewport_new_gc ( vp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
850
851   return vdl;
852 }
853 /**************************************************************
854  **** SOURCES & DOWNLOADING
855  **************************************************************/
856 typedef struct {
857   gchar *dest;
858   gdouble lat, lon;
859
860   GMutex *mutex;
861   VikDEMLayer *vdl; /* NULL if not alive */
862
863   guint source;
864 } DEMDownloadParams;
865
866
867 /**************************************************
868  *  SOURCE: SRTM                                  *
869  **************************************************/
870
871 static void srtm_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
872 {
873   gint intlat, intlon;
874   const gchar *continent_dir;
875
876   intlat = (int)floor(p->lat);
877   intlon = (int)floor(p->lon);
878   continent_dir = srtm_continent_dir(intlat, intlon);
879
880   if (!continent_dir) {
881     g_warning(N_("No SRTM data available for %f, %f"), p->lat, p->lon);
882     return;
883   }
884
885   gchar *src_fn = g_strdup_printf("%s%s/%c%02d%c%03d.hgt.zip",
886                 SRTM_HTTP_URI,
887                 continent_dir,
888                 (intlat >= 0) ? 'N' : 'S',
889                 ABS(intlat),
890                 (intlon >= 0) ? 'E' : 'W',
891                 ABS(intlon) );
892
893   static DownloadMapOptions options = { FALSE, FALSE, NULL, 0, a_check_map_file };
894   a_http_download_get_url ( SRTM_HTTP_SITE, src_fn, p->dest, &options, NULL );
895   g_free ( src_fn );
896 }
897
898 static gchar *srtm_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
899 {
900   gint intlat, intlon;
901   const gchar *continent_dir;
902
903   intlat = (int)floor(lat);
904   intlon = (int)floor(lon);
905   continent_dir = srtm_continent_dir(intlat, intlon);
906
907   if (!continent_dir)
908     continent_dir = "nowhere";
909
910   return g_strdup_printf("srtm3-%s%s%c%02d%c%03d.hgt.zip",
911                 continent_dir,
912                 G_DIR_SEPARATOR_S,
913                 (intlat >= 0) ? 'N' : 'S',
914                 ABS(intlat),
915                 (intlon >= 0) ? 'E' : 'W',
916                 ABS(intlon) );
917
918 }
919
920 /* TODO: generalize */
921 static void srtm_draw_existence ( VikViewport *vp )
922 {
923   gdouble max_lat, max_lon, min_lat, min_lon;  
924   gchar buf[strlen(MAPS_CACHE_DIR)+strlen(SRTM_CACHE_TEMPLATE)+30];
925   gint i, j;
926
927   vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
928
929   for (i = floor(min_lat); i <= floor(max_lat); i++) {
930     for (j = floor(min_lon); j <= floor(max_lon); j++) {
931       const gchar *continent_dir;
932       if ((continent_dir = srtm_continent_dir(i, j)) == NULL)
933         continue;
934       g_snprintf(buf, sizeof(buf), SRTM_CACHE_TEMPLATE,
935                 MAPS_CACHE_DIR,
936                 continent_dir,
937                 G_DIR_SEPARATOR_S,
938                 (i >= 0) ? 'N' : 'S',
939                 ABS(i),
940                 (j >= 0) ? 'E' : 'W',
941                 ABS(j) );
942       if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
943         VikCoord ne, sw;
944         gint x1, y1, x2, y2;
945         sw.north_south = i;
946         sw.east_west = j;
947         sw.mode = VIK_COORD_LATLON;
948         ne.north_south = i+1;
949         ne.east_west = j+1;
950         ne.mode = VIK_COORD_LATLON;
951         vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
952         vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
953         if ( x1 < 0 ) x1 = 0;
954         if ( y2 < 0 ) y2 = 0;
955         vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc, 
956                 FALSE, x1, y2, x2-x1, y1-y2 );
957       }
958     }
959   }
960 }
961
962
963 /**************************************************
964  *  SOURCE: USGS 24K                              *
965  **************************************************/
966
967 #ifdef VIK_CONFIG_DEM24K
968
969 static void dem24k_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
970 {
971   /* TODO: dest dir */
972   gchar *cmdline = g_strdup_printf("%s %.03f %.03f",
973         DEM24K_DOWNLOAD_SCRIPT,
974         floor(p->lat*8)/8,
975         ceil(p->lon*8)/8 );
976   /* FIX: don't use system, use execv or something. check for existence */
977   system(cmdline);
978 }
979
980 static gchar *dem24k_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
981 {
982   return g_strdup_printf("dem24k/%d/%d/%.03f,%.03f.dem",
983         (gint) lat,
984         (gint) lon,
985         floor(lat*8)/8,
986         ceil(lon*8)/8);
987 }
988
989 /* TODO: generalize */
990 static void dem24k_draw_existence ( VikViewport *vp )
991 {
992   gdouble max_lat, max_lon, min_lat, min_lon;  
993   gchar buf[strlen(MAPS_CACHE_DIR)+40];
994   gdouble i, j;
995
996   vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
997
998   for (i = floor(min_lat*8)/8; i <= floor(max_lat*8)/8; i+=0.125) {
999     /* check lat dir first -- faster */
1000     g_snprintf(buf, sizeof(buf), "%sdem24k/%d/",
1001         MAPS_CACHE_DIR,
1002         (gint) i );
1003     if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1004       continue;
1005     for (j = floor(min_lon*8)/8; j <= floor(max_lon*8)/8; j+=0.125) {
1006       /* check lon dir first -- faster */
1007       g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/",
1008         MAPS_CACHE_DIR,
1009         (gint) i,
1010         (gint) j );
1011       if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1012         continue;
1013       g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/%.03f,%.03f.dem",
1014                 MAPS_CACHE_DIR,
1015                 (gint) i,
1016                 (gint) j,
1017                 floor(i*8)/8,
1018                 floor(j*8)/8 );
1019       if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1020         VikCoord ne, sw;
1021         gint x1, y1, x2, y2;
1022         sw.north_south = i;
1023         sw.east_west = j-0.125;
1024         sw.mode = VIK_COORD_LATLON;
1025         ne.north_south = i+0.125;
1026         ne.east_west = j;
1027         ne.mode = VIK_COORD_LATLON;
1028         vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
1029         vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
1030         if ( x1 < 0 ) x1 = 0;
1031         if ( y2 < 0 ) y2 = 0;
1032         vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc, 
1033                 FALSE, x1, y2, x2-x1, y1-y2 );
1034       }
1035     }
1036   }
1037 }
1038 #endif
1039
1040 /**************************************************
1041  *   SOURCES -- DOWNLOADING & IMPORTING TOOL      *
1042  **************************************************
1043  */
1044
1045 static void weak_ref_cb ( gpointer ptr, GObject * dead_vdl )
1046 {
1047   DEMDownloadParams *p = ptr;
1048   g_mutex_lock ( p->mutex );
1049   p->vdl = NULL;
1050   g_mutex_unlock ( p->mutex );
1051 }
1052
1053 /* Try to add file full_path.
1054  * full_path will be copied.
1055  * returns FALSE if file does not exists, TRUE otherwise.
1056  */
1057 static gboolean dem_layer_add_file ( VikDEMLayer *vdl, const gchar *full_path )
1058 {
1059   if ( g_file_test(full_path, G_FILE_TEST_EXISTS ) == TRUE ) {
1060     /* only load if file size is not 0 (not in progress) */
1061     struct stat sb;
1062     stat (full_path, &sb);
1063     if ( sb.st_size ) {
1064       gchar *duped_path = g_strdup(full_path);
1065       vdl->files = g_list_prepend ( vdl->files, duped_path );
1066       a_dems_load ( duped_path );
1067       g_debug("%s: %s", __FUNCTION__, duped_path);
1068       vik_layer_emit_update ( VIK_LAYER(vdl) );
1069     }
1070     return TRUE;
1071   } else
1072     return FALSE;
1073 }
1074
1075 static void dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
1076 {
1077   if ( p->source == DEM_SOURCE_SRTM )
1078     srtm_dem_download_thread ( p, threaddata );
1079 #ifdef VIK_CONFIG_DEM24K
1080   else if ( p->source == DEM_SOURCE_DEM24K )
1081     dem24k_dem_download_thread ( p, threaddata );
1082 #endif
1083
1084   gdk_threads_enter();
1085   g_mutex_lock ( p->mutex );
1086   if ( p->vdl ) {
1087     g_object_weak_unref ( G_OBJECT(p->vdl), weak_ref_cb, p );
1088
1089     if ( dem_layer_add_file ( p->vdl, p->dest ) )
1090       vik_layer_emit_update ( VIK_LAYER(p->vdl) );
1091   }
1092   g_mutex_unlock ( p->mutex );
1093   gdk_threads_leave();
1094 }
1095
1096
1097 static void free_dem_download_params ( DEMDownloadParams *p )
1098 {
1099   g_mutex_free ( p->mutex );
1100   g_free ( p->dest );
1101   g_free ( p );
1102 }
1103
1104 static gpointer dem_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1105 {
1106   return vvp;
1107 }
1108
1109
1110 static gboolean dem_layer_download_release ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1111 {
1112   VikCoord coord;
1113   struct LatLon ll;
1114
1115   gchar *full_path;
1116   gchar *dem_file = NULL;
1117
1118   if ( vdl->source == DEM_SOURCE_NONE )
1119     a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vdl), _("No download source selected. Edit layer properties.") );
1120
1121   vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1122   vik_coord_to_latlon ( &coord, &ll );
1123
1124   
1125   if ( vdl->source == DEM_SOURCE_SRTM )
1126     dem_file = srtm_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1127 #ifdef VIK_CONFIG_DEM24K
1128   else if ( vdl->source == DEM_SOURCE_DEM24K )
1129     dem_file = dem24k_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1130 #endif
1131
1132   if ( ! dem_file )
1133     return TRUE;
1134
1135   full_path = g_strdup_printf("%s%s", MAPS_CACHE_DIR, dem_file );
1136
1137   g_debug("%s: %s", __FUNCTION__, full_path);
1138
1139   // TODO: check if already in filelist
1140
1141   if ( ! dem_layer_add_file(vdl, full_path) ) {
1142     gchar *tmp = g_strdup_printf ( _("Downloading DEM %s"), dem_file );
1143     DEMDownloadParams *p = g_malloc(sizeof(DEMDownloadParams));
1144     p->dest = g_strdup(full_path);
1145     p->lat = ll.lat;
1146     p->lon = ll.lon;
1147     p->vdl = vdl;
1148     p->mutex = g_mutex_new();
1149     p->source = vdl->source;
1150     g_object_weak_ref(G_OBJECT(p->vdl), weak_ref_cb, p );
1151
1152     a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vdl), tmp,
1153                 (vik_thr_func) dem_download_thread, p,
1154                 (vik_thr_free_func) free_dem_download_params, NULL, 1 );
1155
1156     g_free ( tmp );
1157   }
1158
1159   g_free ( dem_file );
1160   g_free ( full_path );
1161
1162   return TRUE;
1163 }
1164
1165 static gboolean dem_layer_download_click ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1166 {
1167 /* choose & keep track of cache dir
1168  * download in background thread
1169  * download over area */
1170   return TRUE;
1171 }
1172
1173