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