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