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