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