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