]> git.street.me.uk Git - andy/viking.git/blame - src/vikdemlayer.c
[DOC] Mention XML configuration can override internal defaults.
[andy/viking.git] / src / vikdemlayer.c
CommitLineData
ad0a8c2d
EB
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 */
4c77d5e0
GB
21#ifdef HAVE_CONFIG_H
22#include "config.h"
23#endif
8c00358d
GB
24
25#ifdef HAVE_MATH_H
ad0a8c2d 26#include <math.h>
8c00358d
GB
27#endif
28#ifdef HAVE_SYS_TYPES_H
0c1044e9 29#include <sys/types.h>
8c00358d
GB
30#endif
31#ifdef HAVE_SYS_STAT_H
0c1044e9 32#include <sys/stat.h>
8c00358d
GB
33#endif
34#ifdef HAVE_STRING_H
0c1044e9 35#include <string.h>
8c00358d 36#endif
8c721f83 37#include <stdlib.h>
13d3fc89 38#include <glib/gstdio.h>
4c77d5e0 39#include <glib/gi18n.h>
ad0a8c2d 40
0c1044e9 41#include "background.h"
a7023a1b 42#include "viking.h"
c96fce26 43#include "vikmapslayer.h"
ad0a8c2d 44#include "vikdemlayer.h"
ad0a8c2d
EB
45#include "dem.h"
46#include "dems.h"
bce3a7b0
EB
47#include "icons/icons.h"
48
8c721f83 49#define MAPS_CACHE_DIR maps_layer_default_dir()
0e25c0d0 50#define SRTM_CACHE_TEMPLATE "%ssrtm3-%s%s%c%02d%c%03d.hgt.zip"
6308aeed 51
aa4c69af 52#define SRTM_HTTP_BASE_URL "https://dds.cr.usgs.gov/srtm/version2_1/SRTM3"
6308aeed
RN
53static gchar *base_url = NULL;
54#define VIK_SETTINGS_SRTM_HTTP_BASE_URL "srtm_http_base_url"
8c721f83
EB
55
56#ifdef VIK_CONFIG_DEM24K
57#define DEM24K_DOWNLOAD_SCRIPT "dem24k.pl"
58#endif
59
7e74e26b 60#define UNUSED_LINE_THICKNESS 3
0c1044e9 61
0c025cfa
RN
62static VikDEMLayer *dem_layer_new ( VikViewport *vvp );
63static void dem_layer_draw ( VikDEMLayer *vdl, VikViewport *vp );
64static void dem_layer_free ( VikDEMLayer *vdl );
65static VikDEMLayer *dem_layer_create ( VikViewport *vp );
e6c9ae37 66static const gchar* dem_layer_tooltip( VikDEMLayer *vdl );
ad0a8c2d
EB
67static void dem_layer_marshall( VikDEMLayer *vdl, guint8 **data, gint *len );
68static VikDEMLayer *dem_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
158b3642
RN
69static gboolean dem_layer_set_param ( VikDEMLayer *vdl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
70static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gboolean is_file_operation );
07059501 71static void dem_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file );
8c721f83
EB
72static void srtm_draw_existence ( VikViewport *vp );
73
74#ifdef VIK_CONFIG_DEM24K
75static void dem24k_draw_existence ( VikViewport *vp );
76#endif
ad0a8c2d 77
0cabb401 78/* Upped upper limit incase units are feet */
ad0a8c2d 79static VikLayerParamScale param_scales[] = {
0cabb401
RN
80 { 0, 30000, 10, 1 },
81 { 1, 30000, 10, 1 },
ad0a8c2d
EB
82};
83
8c721f83 84static gchar *params_source[] = {
14dd983a 85 N_("SRTM Global 90m (3 arcsec)"),
8c721f83
EB
86#ifdef VIK_CONFIG_DEM24K
87 "USA 10m (USGS 24k)",
88#endif
4235f369 89 NULL
8c721f83
EB
90 };
91
c07088dd 92static gchar *params_type[] = {
c3c75563
GB
93 N_("Absolute height"),
94 N_("Height gradient"),
c07088dd
DRP
95 NULL
96};
97
8c721f83
EB
98enum { DEM_SOURCE_SRTM,
99#ifdef VIK_CONFIG_DEM24K
100 DEM_SOURCE_DEM24K,
101#endif
8c721f83
EB
102 };
103
c07088dd
DRP
104enum { DEM_TYPE_HEIGHT = 0,
105 DEM_TYPE_GRADIENT,
106 DEM_TYPE_NONE,
107};
108
a7023a1b
RN
109static VikLayerParamData color_default ( void ) {
110 VikLayerParamData data; gdk_color_parse ( "blue", &data.c ); return data;
111}
112
113static VikLayerParamData source_default ( void ) { return VIK_LPD_UINT ( DEM_SOURCE_SRTM ); }
114static VikLayerParamData type_default ( void ) { return VIK_LPD_UINT ( DEM_TYPE_HEIGHT ); }
115static VikLayerParamData min_elev_default ( void ) { return VIK_LPD_DOUBLE ( 0.0 ); }
116static VikLayerParamData max_elev_default ( void ) { return VIK_LPD_DOUBLE ( 1000.0 ); }
117
ad0a8c2d 118static VikLayerParam dem_layer_params[] = {
a87f8fa1
RN
119 { VIK_LAYER_DEM, "files", VIK_LAYER_PARAM_STRING_LIST, VIK_LAYER_GROUP_NONE, N_("DEM Files:"), VIK_LAYER_WIDGET_FILELIST, NULL, NULL, NULL, NULL, NULL, NULL },
120 { 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, NULL, NULL },
121 { 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, NULL, NULL },
122 { 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, NULL, NULL },
123 { 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, NULL, NULL },
124 { 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, NULL, NULL },
ad0a8c2d
EB
125};
126
8c721f83 127
7e74e26b 128enum { PARAM_FILES=0, PARAM_SOURCE, PARAM_COLOR, PARAM_TYPE, PARAM_MIN_ELEV, PARAM_MAX_ELEV, NUM_PARAMS };
8c721f83 129
0c1044e9
EB
130static gpointer dem_layer_download_create ( VikWindow *vw, VikViewport *vvp);
131static gboolean dem_layer_download_release ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp );
132static gboolean dem_layer_download_click ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp );
133
134static VikToolInterface dem_tools[] = {
95de0de7
RN
135 { &demdl_18_pixbuf,
136 { "DEMDownload", "vik-icon-DEM Download", N_("_DEM Download"), NULL, N_("DEM Download"), 0 },
79dce0cb 137 (VikToolConstructorFunc) dem_layer_download_create, NULL, NULL, NULL,
bce3a7b0 138 (VikToolMouseFunc) dem_layer_download_click, NULL, (VikToolMouseFunc) dem_layer_download_release,
ef5e8132
RN
139 (VikToolKeyFunc) NULL,
140 FALSE,
63959706 141 GDK_CURSOR_IS_PIXMAP, &cursor_demdl_pixbuf, NULL },
0c1044e9
EB
142};
143
144
7e74e26b
RN
145/* HEIGHT COLORS
146 The first entry is blue for a default 'sea' colour,
147 however the value used by the corresponding gc can be configured as part of the DEM layer properties.
148 The other colours, shaded from brown to white are used to give an indication of height.
ad0a8c2d 149*/
c07088dd 150static gchar *dem_height_colors[] = {
ad0a8c2d 151"#0000FF",
c07088dd
DRP
152"#9b793c", "#9c7d40", "#9d8144", "#9e8549", "#9f894d", "#a08d51", "#a29156", "#a3955a", "#a4995e", "#a69d63",
153"#a89f65", "#aaa267", "#ada569", "#afa76b", "#b1aa6d", "#b4ad6f", "#b6b071", "#b9b373", "#bcb676", "#beb978",
154"#c0bc7a", "#c2c07d", "#c4c37f", "#c6c681", "#c8ca84", "#cacd86", "#ccd188", "#cfd58b", "#c2ce84", "#b5c87e",
155"#a9c278", "#9cbb71", "#8fb56b", "#83af65", "#76a95e", "#6aa358", "#5e9d52", "#63a055", "#69a458", "#6fa85c",
156"#74ac5f", "#7ab063", "#80b467", "#86b86a", "#8cbc6e", "#92c072", "#94c175", "#97c278", "#9ac47c", "#9cc57f",
157"#9fc682", "#a2c886", "#a4c989", "#a7cb8d", "#aacd91", "#afce99", "#b5d0a1", "#bbd2aa", "#c0d3b2", "#c6d5ba",
158"#ccd7c3", "#d1d9cb", "#d7dbd4", "#DDDDDD", "#e0e0e0", "#e4e4e4", "#e8e8e8", "#ebebeb", "#efefef", "#f3f3f3",
159"#f7f7f7", "#fbfbfb", "#ffffff"
ad0a8c2d
EB
160};
161
c07088dd
DRP
162static const guint DEM_N_HEIGHT_COLORS = sizeof(dem_height_colors)/sizeof(dem_height_colors[0]);
163
ad0a8c2d 164/*
c07088dd
DRP
165"#9b793c", "#9e8549", "#a29156", "#a69d63", "#ada569", "#b4ad6f", "#bcb676", "#c2c07d", "#c8ca84", "#cfd58b",
166"#a9c278", "#83af65", "#5e9d52", "#6fa85c", "#80b467", "#92c072", "#9ac47c", "#a2c886", "#aacd91", "#bbd2aa",
167"#ccd7c3", "#DDDDDD", "#e8e8e8", "#f3f3f3", "#FFFFFF"
ad0a8c2d
EB
168};
169*/
170
c07088dd 171static gchar *dem_gradient_colors[] = {
0251a183 172"#AAAAAA",
c07088dd
DRP
173"#000000", "#000011", "#000022", "#000033", "#000044", "#00004c", "#000055", "#00005d", "#000066", "#00006e",
174"#000077", "#00007f", "#000088", "#000090", "#000099", "#0000a1", "#0000aa", "#0000b2", "#0000bb", "#0000c3",
175"#0000cc", "#0000d4", "#0000dd", "#0000e5", "#0000ee", "#0000f6", "#0000ff", "#0008f7", "#0011ee", "#0019e6",
176"#0022dd", "#002ad5", "#0033cc", "#003bc4", "#0044bb", "#004cb3", "#0055aa", "#005da2", "#006699", "#006e91",
177"#007788", "#007f80", "#008877", "#00906f", "#009966", "#00a15e", "#00aa55", "#00b24d", "#00bb44", "#00c33c",
178"#00cc33", "#00d42b", "#00dd22", "#00e51a", "#00ee11", "#00f609", "#00ff00", "#08f700", "#11ee00", "#19e600",
179"#22dd00", "#2ad500", "#33cc00", "#3bc400", "#44bb00", "#4cb300", "#55aa00", "#5da200", "#669900", "#6e9100",
180"#778800", "#7f8000", "#887700", "#906f00", "#996600", "#a15e00", "#aa5500", "#b24d00", "#bb4400", "#c33c00",
181"#cc3300", "#d42b00", "#dd2200", "#e51a00", "#ee1100", "#f60900", "#ff0000",
182"#FFFFFF"
183};
184
185static const guint DEM_N_GRADIENT_COLORS = sizeof(dem_gradient_colors)/sizeof(dem_gradient_colors[0]);
ad0a8c2d 186
ad0a8c2d
EB
187
188VikLayerInterface vik_dem_layer_interface = {
db386630 189 "DEM",
affcc0f2 190 N_("DEM"),
75078768 191 "<control><shift>D",
5bfafde9 192 &vikdemlayer_pixbuf,
ad0a8c2d 193
0c1044e9
EB
194 dem_tools,
195 sizeof(dem_tools) / sizeof(dem_tools[0]),
ad0a8c2d
EB
196
197 dem_layer_params,
198 NUM_PARAMS,
199 NULL,
200 0,
201
202 VIK_MENU_ITEM_ALL,
203
0c025cfa 204 (VikLayerFuncCreate) dem_layer_create,
ad0a8c2d 205 (VikLayerFuncRealize) NULL,
b112cbf5 206 (VikLayerFuncPostRead) dem_layer_post_read,
0c025cfa 207 (VikLayerFuncFree) dem_layer_free,
ad0a8c2d
EB
208
209 (VikLayerFuncProperties) NULL,
0c025cfa 210 (VikLayerFuncDraw) dem_layer_draw,
ad0a8c2d
EB
211 (VikLayerFuncChangeCoordMode) NULL,
212
03c97bc3
RN
213 (VikLayerFuncGetTimestamp) NULL,
214
ad0a8c2d
EB
215 (VikLayerFuncSetMenuItemsSelection) NULL,
216 (VikLayerFuncGetMenuItemsSelection) NULL,
217
218 (VikLayerFuncAddMenuItems) NULL,
219 (VikLayerFuncSublayerAddMenuItems) NULL,
220
221 (VikLayerFuncSublayerRenameRequest) NULL,
222 (VikLayerFuncSublayerToggleVisible) NULL,
9da7faf2 223 (VikLayerFuncSublayerTooltip) NULL,
e6c9ae37 224 (VikLayerFuncLayerTooltip) dem_layer_tooltip,
a5dcfdb7 225 (VikLayerFuncLayerSelected) NULL,
ad0a8c2d 226
ad0a8c2d
EB
227 (VikLayerFuncMarshall) dem_layer_marshall,
228 (VikLayerFuncUnmarshall) dem_layer_unmarshall,
229
230 (VikLayerFuncSetParam) dem_layer_set_param,
231 (VikLayerFuncGetParam) dem_layer_get_param,
db43cfa4 232 (VikLayerFuncChangeParam) NULL,
ad0a8c2d
EB
233
234 (VikLayerFuncReadFileData) NULL,
235 (VikLayerFuncWriteFileData) NULL,
236
237 (VikLayerFuncDeleteItem) NULL,
d5874ef9 238 (VikLayerFuncCutItem) NULL,
ad0a8c2d
EB
239 (VikLayerFuncCopyItem) NULL,
240 (VikLayerFuncPasteItem) NULL,
241 (VikLayerFuncFreeCopiedItem) NULL,
242 (VikLayerFuncDragDropRequest) NULL,
77ad64fa
RN
243
244 (VikLayerFuncSelectClick) NULL,
08f14055
RN
245 (VikLayerFuncSelectMove) NULL,
246 (VikLayerFuncSelectRelease) NULL,
e46f259a 247 (VikLayerFuncSelectedViewportMenu) NULL,
ad0a8c2d
EB
248};
249
250struct _VikDEMLayer {
251 VikLayer vl;
ad0a8c2d 252 GdkGC **gcs;
c07088dd 253 GdkGC **gcsgradient;
ad0a8c2d 254 GList *files;
415302f4 255 gdouble min_elev;
ad0a8c2d 256 gdouble max_elev;
a7023a1b 257 GdkColor color;
8c721f83 258 guint source;
c07088dd 259 guint type;
13d3fc89
RN
260
261 // right click menu only stuff - similar to mapslayer
262 GtkMenu *right_click_menu;
ad0a8c2d
EB
263};
264
6308aeed
RN
265// NB Only performed once per program run
266static void vik_dem_class_init ( VikDEMLayerClass *klass )
267{
268 // Note if suppling your own base URL - the site must still follow the Continent directory layout
269 if ( ! a_settings_get_string ( VIK_SETTINGS_SRTM_HTTP_BASE_URL, &base_url ) ) {
270 // Otherwise use the default
271 base_url = g_strdup ( SRTM_HTTP_BASE_URL );
272 }
273}
274
ad0a8c2d
EB
275GType vik_dem_layer_get_type ()
276{
277 static GType vdl_type = 0;
278
279 if (!vdl_type)
280 {
281 static const GTypeInfo vdl_info =
282 {
283 sizeof (VikDEMLayerClass),
284 NULL, /* base_init */
285 NULL, /* base_finalize */
6308aeed 286 (GClassInitFunc) vik_dem_class_init, /* class init */
ad0a8c2d
EB
287 NULL, /* class_finalize */
288 NULL, /* class_data */
289 sizeof (VikDEMLayer),
290 0,
291 NULL /* instance init */
292 };
293 vdl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikDEMLayer", &vdl_info, 0 );
294 }
295
296 return vdl_type;
297}
298
e6c9ae37
RN
299static const gchar* dem_layer_tooltip( VikDEMLayer *vdl )
300{
301 static gchar tmp_buf[100];
302 g_snprintf (tmp_buf, sizeof(tmp_buf), _("Number of files: %d"), g_list_length (vdl->files));
303 return tmp_buf;
304}
305
ad0a8c2d
EB
306static void dem_layer_marshall( VikDEMLayer *vdl, guint8 **data, gint *len )
307{
308 vik_layer_marshall_params ( VIK_LAYER(vdl), data, len );
309}
310
311static VikDEMLayer *dem_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
312{
0c025cfa 313 VikDEMLayer *rv = dem_layer_new ( vvp );
fa86f1c0
EB
314 gint i;
315
316 /* TODO: share GCS between layers */
a7023a1b
RN
317 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ ) {
318 if ( i == 0 )
319 rv->gcs[i] = vik_viewport_new_gc_from_color ( vvp, &(rv->color), UNUSED_LINE_THICKNESS );
320 else
321 rv->gcs[i] = vik_viewport_new_gc ( vvp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
322 }
c07088dd 323 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
7e74e26b 324 rv->gcsgradient[i] = vik_viewport_new_gc ( vvp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
fa86f1c0 325
ad0a8c2d
EB
326 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
327 return rv;
328}
329
29392f59
RN
330/* Structure for DEM data used in background thread */
331typedef struct {
332 VikDEMLayer *vdl;
333} dem_load_thread_data;
334
335/*
336 * Function for starting the DEM file loading as a background thread
337 */
338static int dem_layer_load_list_thread ( dem_load_thread_data *dltd, gpointer threaddata )
339{
340 int result = 0; // Default to good
341 // Actual Load
342 if ( a_dems_load_list ( &(dltd->vdl->files), threaddata ) ) {
343 // Thread cancelled
344 result = -1;
345 }
346
347 // ATM as each file is processed the screen is not updated (no mechanism exposed to a_dems_load_list)
348 // Thus force draw only at the end, as loading is complete/aborted
94595b61
RN
349 // Test is helpful to prevent Gtk-CRITICAL warnings if the program is exitted whilst loading
350 if ( IS_VIK_LAYER(dltd->vdl) )
d3b7baa7 351 vik_layer_emit_update ( VIK_LAYER(dltd->vdl) ); // NB update requested from background thread
29392f59
RN
352
353 return result;
354}
355
356static void dem_layer_thread_data_free ( dem_load_thread_data *data )
357{
358 // Simple release
359 g_free ( data );
360}
361
362static void dem_layer_thread_cancel ( dem_load_thread_data *data )
363{
364 // Abort loading
365 // Instead of freeing the list, leave it as partially processed
366 // Thus we can see/use what was done
367}
368
88542aa9
RN
369/**
370 * Process the list of DEM files and convert each one to a relative path
371 */
372static GList *dem_layer_convert_to_relative_filenaming ( GList *files )
373{
374 gchar *cwd = g_get_current_dir();
375 if ( !cwd )
376 return files;
377
378 GList *relfiles = NULL;
379
380 while ( files ) {
381 gchar *file = g_strdup ( file_GetRelativeFilename ( cwd, files->data ) );
382 relfiles = g_list_prepend ( relfiles, file );
383 files = files->next;
384 }
385
386 g_free ( cwd );
387
388 if ( relfiles ) {
389 // Replacing current list, so delete old values first.
390 GList *iter = files;
391 while ( iter ) {
392 g_free ( iter->data );
393 iter = iter->next;
394 }
395 g_list_free ( files );
396
397 return relfiles;
398 }
399
400 return files;
401}
402
158b3642 403gboolean dem_layer_set_param ( VikDEMLayer *vdl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
ad0a8c2d
EB
404{
405 switch ( id )
406 {
a7023a1b 407 case PARAM_COLOR: vdl->color = data.c; gdk_gc_set_rgb_fg_color ( vdl->gcs[0], &(vdl->color) ); break;
8c721f83 408 case PARAM_SOURCE: vdl->source = data.u; break;
c07088dd 409 case PARAM_TYPE: vdl->type = data.u; break;
0cabb401
RN
410 case PARAM_MIN_ELEV:
411 /* Convert to store internally
412 NB file operation always in internal units (metres) */
413 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
ab1e0693 414 vdl->min_elev = VIK_FEET_TO_METERS(data.d);
0cabb401
RN
415 else
416 vdl->min_elev = data.d;
417 break;
418 case PARAM_MAX_ELEV:
419 /* Convert to store internally
420 NB file operation always in internal units (metres) */
421 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
ab1e0693 422 vdl->max_elev = VIK_FEET_TO_METERS(data.d);
0cabb401
RN
423 else
424 vdl->max_elev = data.d;
425 break;
29392f59
RN
426 case PARAM_FILES:
427 {
428 // Clear out old settings - if any commonalities with new settings they will have to be read again
429 a_dems_list_free ( vdl->files );
430 // Set file list so any other intermediate screen drawing updates will show currently loaded DEMs by the working thread
431 vdl->files = data.sl;
09c91d84
RN
432 // No need for thread if no files
433 if ( vdl->files ) {
434 // Thread Load
435 dem_load_thread_data *dltd = g_malloc ( sizeof(dem_load_thread_data) );
436 dltd->vdl = vdl;
437 dltd->vdl->files = data.sl;
438
c75da936
RN
439 a_background_thread ( BACKGROUND_POOL_LOCAL,
440 VIK_GTK_WINDOW_FROM_WIDGET(vp),
09c91d84
RN
441 _("DEM Loading"),
442 (vik_thr_func) dem_layer_load_list_thread,
443 dltd,
444 (vik_thr_free_func) dem_layer_thread_data_free,
445 (vik_thr_free_func) dem_layer_thread_cancel,
446 g_list_length ( data.sl ) ); // Number of DEM files
447 }
29392f59
RN
448 break;
449 }
9351abc4 450 default: break;
ad0a8c2d
EB
451 }
452 return TRUE;
453}
454
158b3642 455static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gboolean is_file_operation )
ad0a8c2d
EB
456{
457 VikLayerParamData rv;
458 switch ( id )
459 {
88542aa9
RN
460 case PARAM_FILES:
461 rv.sl = vdl->files;
462 if ( is_file_operation )
463 // Save in relative format if necessary
464 if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE )
465 rv.sl = dem_layer_convert_to_relative_filenaming ( rv.sl );
466 break;
8c721f83 467 case PARAM_SOURCE: rv.u = vdl->source; break;
c07088dd 468 case PARAM_TYPE: rv.u = vdl->type; break;
a7023a1b 469 case PARAM_COLOR: rv.c = vdl->color; break;
0cabb401
RN
470 case PARAM_MIN_ELEV:
471 /* Convert for display in desired units
472 NB file operation always in internal units (metres) */
473 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
6c20e59a 474 rv.d = VIK_METERS_TO_FEET(vdl->min_elev);
0cabb401
RN
475 else
476 rv.d = vdl->min_elev;
477 break;
478 case PARAM_MAX_ELEV:
479 /* Convert for display in desired units
480 NB file operation always in internal units (metres) */
481 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
6c20e59a 482 rv.d = VIK_METERS_TO_FEET(vdl->max_elev);
0cabb401
RN
483 else
484 rv.d = vdl->max_elev;
485 break;
9351abc4 486 default: break;
ad0a8c2d
EB
487 }
488 return rv;
489}
490
07059501 491static void dem_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file )
ad0a8c2d 492{
7e74e26b 493 /* nothing ATM, but keep in case it's needed the future */
ad0a8c2d
EB
494}
495
0c025cfa 496static VikDEMLayer *dem_layer_new ( VikViewport *vvp )
ad0a8c2d
EB
497{
498 VikDEMLayer *vdl = VIK_DEM_LAYER ( g_object_new ( VIK_DEM_LAYER_TYPE, NULL ) );
fa86f1c0 499
a0c65899 500 vik_layer_set_type ( VIK_LAYER(vdl), VIK_LAYER_DEM );
ad0a8c2d
EB
501
502 vdl->files = NULL;
503
c07088dd
DRP
504 vdl->gcs = g_malloc(sizeof(GdkGC *)*DEM_N_HEIGHT_COLORS);
505 vdl->gcsgradient = g_malloc(sizeof(GdkGC *)*DEM_N_GRADIENT_COLORS);
fa86f1c0 506 /* make new gcs only if we need it (copy layer -> use old) */
ad0a8c2d 507
a7023a1b
RN
508 // Ensure the base GC is available so the default colour can be applied
509 if ( vvp ) vdl->gcs[0] = vik_viewport_new_gc ( vvp, "#0000FF", 1 );
510
511 vik_layer_set_defaults ( VIK_LAYER(vdl), vvp );
512
ad0a8c2d
EB
513 return vdl;
514}
515
516
f9518bc9
DRP
517static inline guint16 get_height_difference(gint16 elev, gint16 new_elev)
518{
519 if(new_elev == VIK_DEM_INVALID_ELEVATION)
520 return 0;
521 else
522 return abs(new_elev - elev);
523}
524
0c1044e9 525
ad0a8c2d
EB
526static void vik_dem_layer_draw_dem ( VikDEMLayer *vdl, VikViewport *vp, VikDEM *dem )
527{
f9518bc9 528 VikDEMColumn *column, *prevcolumn, *nextcolumn;
ad0a8c2d
EB
529
530 struct LatLon dem_northeast, dem_southwest;
f9518bc9 531 gdouble max_lat, max_lon, min_lat, min_lon;
ad0a8c2d 532
ad0a8c2d
EB
533 /**** Check if viewport and DEM data overlap ****/
534
535 /* get min, max lat/lon of viewport */
0c1044e9 536 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
ad0a8c2d
EB
537
538 /* get min, max lat/lon of DEM data */
539 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
540 dem_northeast.lat = dem->max_north / 3600.0;
541 dem_northeast.lon = dem->max_east / 3600.0;
542 dem_southwest.lat = dem->min_north / 3600.0;
543 dem_southwest.lon = dem->min_east / 3600.0;
544 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
545 struct UTM dem_northeast_utm, dem_southwest_utm;
546 dem_northeast_utm.northing = dem->max_north;
547 dem_northeast_utm.easting = dem->max_east;
548 dem_southwest_utm.northing = dem->min_north;
549 dem_southwest_utm.easting = dem->min_east;
550 dem_northeast_utm.zone = dem_southwest_utm.zone = dem->utm_zone;
551 dem_northeast_utm.letter = dem_southwest_utm.letter = dem->utm_letter;
552
553 a_coords_utm_to_latlon(&dem_northeast_utm, &dem_northeast);
554 a_coords_utm_to_latlon(&dem_southwest_utm, &dem_southwest);
00ec5f5c
RN
555 } else {
556 // Unknown horiz_units - this shouldn't normally happen
557 // Thus can't work out positions to use
558 return;
ad0a8c2d
EB
559 }
560
561 if ( (max_lat > dem_northeast.lat && min_lat > dem_northeast.lat) ||
562 (max_lat < dem_southwest.lat && min_lat < dem_southwest.lat) )
563 return;
564 else if ( (max_lon > dem_northeast.lon && min_lon > dem_northeast.lon) ||
565 (max_lon < dem_southwest.lon && min_lon < dem_southwest.lon) )
566 return;
567 /* else they overlap */
568
569 /**** End Overlap Check ****/
0c1044e9
EB
570 /* boxes to show where we have DEM instead of actually drawing the DEM.
571 * useful if we want to see what areas we have coverage for (if we want
572 * to get elevation data for a track) but don't want to cover the map.
573 */
574
575 #if 0
576 /* draw a box if a DEM is loaded. in future I'd like to add an option for this
577 * this is useful if we want to see what areas we have dem for but don't want to
578 * cover the map (or maybe we just need translucent DEM?) */
579 {
580 VikCoord demne, demsw;
581 gint x1, y1, x2, y2;
582 vik_coord_load_from_latlon(&demne, vik_viewport_get_coord_mode(vp), &dem_northeast);
583 vik_coord_load_from_latlon(&demsw, vik_viewport_get_coord_mode(vp), &dem_southwest);
584
585 vik_viewport_coord_to_screen ( vp, &demne, &x1, &y1 );
586 vik_viewport_coord_to_screen ( vp, &demsw, &x2, &y2 );
587
588 if ( x1 > vik_viewport_get_width(vp) ) x1=vik_viewport_get_width(vp);
589 if ( y2 > vik_viewport_get_height(vp) ) y2=vik_viewport_get_height(vp);
590 if ( x2 < 0 ) x2 = 0;
591 if ( y1 < 0 ) y1 = 0;
ff37db21 592 vik_viewport_draw_rectangle ( vp, gtk_widget_get_style(GTK_WIDGET(vp))->black_gc,
0c1044e9 593 FALSE, x2, y1, x1-x2, y2-y1 );
0c1044e9
EB
594 return;
595 }
596 #endif
ad0a8c2d
EB
597
598 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
599 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
600
601 gdouble max_lat_as, max_lon_as, min_lat_as, min_lon_as;
602 gdouble start_lat_as, end_lat_as, start_lon_as, end_lon_as;
603
604 gdouble start_lat, end_lat, start_lon, end_lon;
605
606 struct LatLon counter;
607
608 guint x, y, start_x, start_y;
609
610 gint16 elev;
611
f9518bc9 612 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 80 ); /* todo: smarter calculation. */
ad0a8c2d
EB
613
614 gdouble nscale_deg = dem->north_scale / ((gdouble) 3600);
615 gdouble escale_deg = dem->east_scale / ((gdouble) 3600);
616
617 max_lat_as = max_lat * 3600;
618 min_lat_as = min_lat * 3600;
619 max_lon_as = max_lon * 3600;
620 min_lon_as = min_lon * 3600;
621
622 start_lat_as = MAX(min_lat_as, dem->min_north);
623 end_lat_as = MIN(max_lat_as, dem->max_north);
624 start_lon_as = MAX(min_lon_as, dem->min_east);
625 end_lon_as = MIN(max_lon_as, dem->max_east);
626
627 start_lat = floor(start_lat_as / dem->north_scale) * nscale_deg;
628 end_lat = ceil (end_lat_as / dem->north_scale) * nscale_deg;
629 start_lon = floor(start_lon_as / dem->east_scale) * escale_deg;
630 end_lon = ceil (end_lon_as / dem->east_scale) * escale_deg;
631
632 vik_dem_east_north_to_xy ( dem, start_lon_as, start_lat_as, &start_x, &start_y );
f9518bc9
DRP
633 guint gradient_skip_factor = 1;
634 if(vdl->type == DEM_TYPE_GRADIENT)
635 gradient_skip_factor = skip_factor;
ad0a8c2d 636
415302f4
EB
637 /* verify sane elev interval */
638 if ( vdl->max_elev <= vdl->min_elev )
639 vdl->max_elev = vdl->min_elev + 1;
640
f9518bc9
DRP
641 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 ) {
642 // NOTE: ( counter.lon <= end_lon + ESCALE_DEG*SKIP_FACTOR ) is neccessary so in high zoom modes,
643 // the leftmost column does also get drawn, if the center point is out of viewport.
5e610fc3 644 if ( x < dem->n_columns ) {
ad0a8c2d 645 column = g_ptr_array_index ( dem->columns, x );
f9518bc9
DRP
646 // get previous and next column. catch out-of-bound.
647 gint32 new_x = x;
648 new_x -= gradient_skip_factor;
649 if(new_x < 1)
650 prevcolumn = g_ptr_array_index ( dem->columns, x+1);
651 else
652 prevcolumn = g_ptr_array_index ( dem->columns, new_x);
653 new_x = x;
654 new_x += gradient_skip_factor;
655 if(new_x >= dem->n_columns)
656 nextcolumn = g_ptr_array_index ( dem->columns, x-1);
657 else
658 nextcolumn = g_ptr_array_index ( dem->columns, new_x);
c07088dd 659
ad0a8c2d
EB
660 for ( y=start_y, counter.lat = start_lat; counter.lat <= end_lat; counter.lat += nscale_deg * skip_factor, y += skip_factor ) {
661 if ( y > column->n_points )
662 break;
f9518bc9 663
ad0a8c2d 664 elev = column->points[y];
415302f4 665
f9518bc9
DRP
666 // calculate bounding box for drawing
667 gint box_x, box_y, box_width, box_height;
668 struct LatLon box_c;
669 box_c = counter;
670 box_c.lat += (nscale_deg * skip_factor)/2;
671 box_c.lon -= (escale_deg * skip_factor)/2;
672 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
673 vik_viewport_coord_to_screen(vp, &tmp, &box_x, &box_y);
674 // catch box at borders
675 if(box_x < 0)
676 box_x = 0;
677 if(box_y < 0)
678 box_y = 0;
679 box_c.lat -= nscale_deg * skip_factor;
680 box_c.lon += escale_deg * skip_factor;
681 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
682 vik_viewport_coord_to_screen(vp, &tmp, &box_width, &box_height);
683 box_width -= box_x;
684 box_height -= box_y;
685 // catch box at borders
686 if(box_width < 0 || box_height < 0)
e2c37392
RN
687 // skip this as is out of the viewport (e.g. zoomed in so this point is way off screen)
688 continue;
f9518bc9 689
7e74e26b 690 gboolean below_minimum = FALSE;
f9518bc9 691 if(vdl->type == DEM_TYPE_HEIGHT) {
7e74e26b 692 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev ) {
104f8c15
RN
693 // Prevent 'elev - vdl->min_elev' from being negative so can safely use as array index
694 elev = ceil ( vdl->min_elev );
7e74e26b
RN
695 below_minimum = TRUE;
696 }
f9518bc9 697 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
104f8c15 698 elev = vdl->max_elev;
f9518bc9 699 }
415302f4 700
ad0a8c2d 701 {
f9518bc9
DRP
702 if(vdl->type == DEM_TYPE_GRADIENT) {
703 if( elev == VIK_DEM_INVALID_ELEVATION ) {
704 /* don't draw it */
705 } else {
706 // calculate and sum gradient in all directions
707 gint16 change = 0;
708 gint32 new_y;
709
710 // calculate gradient from height points all around the current one
711 new_y = y - gradient_skip_factor;
712 if(new_y < 0)
713 new_y = y;
714 change += get_height_difference(elev, prevcolumn->points[new_y]);
715 change += get_height_difference(elev, column->points[new_y]);
716 change += get_height_difference(elev, nextcolumn->points[new_y]);
717
718 change += get_height_difference(elev, prevcolumn->points[y]);
719 change += get_height_difference(elev, nextcolumn->points[y]);
720
721 new_y = y + gradient_skip_factor;
722 if(new_y >= column->n_points)
723 new_y = y;
724 change += get_height_difference(elev, prevcolumn->points[new_y]);
725 change += get_height_difference(elev, column->points[new_y]);
726 change += get_height_difference(elev, nextcolumn->points[new_y]);
727
728 change = change / ((skip_factor > 1) ? log(skip_factor) : 0.55); // FIXME: better calc.
729
730 if(change < vdl->min_elev)
104f8c15
RN
731 // Prevent 'change - vdl->min_elev' from being negative so can safely use as array index
732 change = ceil ( vdl->min_elev );
f9518bc9
DRP
733
734 if(change > vdl->max_elev)
735 change = vdl->max_elev;
736
737 // void vik_viewport_draw_rectangle ( VikViewport *vvp, GdkGC *gc, gboolean filled, gint x1, gint y1, gint x2, gint y2 );
104f8c15 738 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);
f9518bc9 739 }
c07088dd 740 } else {
f9518bc9
DRP
741 if(vdl->type == DEM_TYPE_HEIGHT) {
742 if ( elev == VIK_DEM_INVALID_ELEVATION )
743 ; /* don't draw it */
7e74e26b
RN
744 else if ( elev <= 0 || below_minimum )
745 /* If 'sea' colour or below the defined mininum draw in the configurable colour */
f9518bc9
DRP
746 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, box_x, box_y, box_width, box_height);
747 else
104f8c15 748 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);
f9518bc9
DRP
749 }
750 }
ad0a8c2d
EB
751 }
752 } /* for y= */
753 }
754 } /* for x= */
755 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
756 gdouble max_nor, max_eas, min_nor, min_eas;
757 gdouble start_nor, start_eas, end_nor, end_eas;
758
759 gint16 elev;
760
761 guint x, y, start_x, start_y;
762
763 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
764 struct UTM counter;
765
766 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 10 ); /* todo: smarter calculation. */
767
0c1044e9
EB
768 VikCoord tleft, tright, bleft, bright;
769
770 vik_viewport_screen_to_coord ( vp, 0, 0, &tleft );
771 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), 0, &tright );
772 vik_viewport_screen_to_coord ( vp, 0, vik_viewport_get_height(vp), &bleft );
773 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), vik_viewport_get_height(vp), &bright );
774
775
ad0a8c2d
EB
776 vik_coord_convert(&tleft, VIK_COORD_UTM);
777 vik_coord_convert(&tright, VIK_COORD_UTM);
778 vik_coord_convert(&bleft, VIK_COORD_UTM);
779 vik_coord_convert(&bright, VIK_COORD_UTM);
780
781 max_nor = MAX(tleft.north_south, tright.north_south);
782 min_nor = MIN(bleft.north_south, bright.north_south);
783 max_eas = MAX(bright.east_west, tright.east_west);
784 min_eas = MIN(bleft.east_west, tleft.east_west);
785
786 start_nor = MAX(min_nor, dem->min_north);
787 end_nor = MIN(max_nor, dem->max_north);
788 if ( tleft.utm_zone == dem->utm_zone && bleft.utm_zone == dem->utm_zone
789 && (tleft.utm_letter >= 'N') == (dem->utm_letter >= 'N')
790 && (bleft.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
791 start_eas = MAX(min_eas, dem->min_east);
792 else
793 start_eas = dem->min_east;
794 if ( tright.utm_zone == dem->utm_zone && bright.utm_zone == dem->utm_zone
795 && (tright.utm_letter >= 'N') == (dem->utm_letter >= 'N')
796 && (bright.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
797 end_eas = MIN(max_eas, dem->max_east);
798 else
799 end_eas = dem->max_east;
800
801 start_nor = floor(start_nor / dem->north_scale) * dem->north_scale;
802 end_nor = ceil (end_nor / dem->north_scale) * dem->north_scale;
803 start_eas = floor(start_eas / dem->east_scale) * dem->east_scale;
804 end_eas = ceil (end_eas / dem->east_scale) * dem->east_scale;
805
806 vik_dem_east_north_to_xy ( dem, start_eas, start_nor, &start_x, &start_y );
807
808 /* TODO: why start_x and start_y are -1 -- rounding error from above? */
809
810 counter.zone = dem->utm_zone;
811 counter.letter = dem->utm_letter;
812
813 for ( x=start_x, counter.easting = start_eas; counter.easting <= end_eas; counter.easting += dem->east_scale * skip_factor, x += skip_factor ) {
814 if ( x > 0 && x < dem->n_columns ) {
815 column = g_ptr_array_index ( dem->columns, x );
816 for ( y=start_y, counter.northing = start_nor; counter.northing <= end_nor; counter.northing += dem->north_scale * skip_factor, y += skip_factor ) {
817 if ( y > column->n_points )
818 continue;
819 elev = column->points[y];
415302f4
EB
820 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev )
821 elev=vdl->min_elev;
822 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
823 elev=vdl->max_elev;
824
ad0a8c2d
EB
825 {
826 gint a, b;
827 vik_coord_load_from_utm(&tmp, vik_viewport_get_coord_mode(vp), &counter);
828 vik_viewport_coord_to_screen(vp, &tmp, &a, &b);
829 if ( elev == VIK_DEM_INVALID_ELEVATION )
830 ; /* don't draw it */
831 else if ( elev <= 0 )
f9518bc9 832 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, a-1, b-1, 2, 2 );
ad0a8c2d 833 else
f9518bc9 834 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 );
ad0a8c2d
EB
835 }
836 } /* for y= */
837 }
838 } /* for x= */
839 }
840}
841
0c1044e9
EB
842/* return the continent for the specified lat, lon */
843/* TODO */
0e25c0d0 844static const gchar *srtm_continent_dir ( gint lat, gint lon )
0c1044e9 845{
0e25c0d0
QT
846 extern const char *_srtm_continent_data[];
847 static GHashTable *srtm_continent = NULL;
848 const gchar *continent;
849 gchar name[16];
850
851 if (!srtm_continent) {
852 const gchar **s;
853
854 srtm_continent = g_hash_table_new(g_str_hash, g_str_equal);
855 s = _srtm_continent_data;
856 while (*s != (gchar *)-1) {
857 continent = *s++;
858 while (*s) {
0654760a 859 g_hash_table_insert(srtm_continent, (gpointer) *s, (gpointer) continent);
0e25c0d0
QT
860 s++;
861 }
862 s++;
863 }
864 }
865 g_snprintf(name, sizeof(name), "%c%02d%c%03d",
866 (lat >= 0) ? 'N' : 'S', ABS(lat),
867 (lon >= 0) ? 'E' : 'W', ABS(lon));
868
869 return(g_hash_table_lookup(srtm_continent, name));
0c1044e9
EB
870}
871
0c025cfa 872static void dem_layer_draw ( VikDEMLayer *vdl, VikViewport *vp )
ad0a8c2d 873{
ad0a8c2d
EB
874 GList *dems_iter = vdl->files;
875 VikDEM *dem;
0c1044e9 876
0c1044e9
EB
877
878 /* search for SRTM3 90m */
8c721f83
EB
879
880 if ( vdl->source == DEM_SOURCE_SRTM )
881 srtm_draw_existence ( vp );
882#ifdef VIK_CONFIG_DEM24K
883 else if ( vdl->source == DEM_SOURCE_DEM24K )
884 dem24k_draw_existence ( vp );
885#endif
0c1044e9 886
ad0a8c2d
EB
887 while ( dems_iter ) {
888 dem = a_dems_get ( (const char *) (dems_iter->data) );
889 if ( dem )
890 vik_dem_layer_draw_dem ( vdl, vp, dem );
891 dems_iter = dems_iter->next;
892 }
893}
894
0c025cfa 895static void dem_layer_free ( VikDEMLayer *vdl )
ad0a8c2d 896{
fa86f1c0 897 gint i;
fa86f1c0 898 if ( vdl->gcs )
c07088dd 899 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
fa86f1c0
EB
900 g_object_unref ( vdl->gcs[i] );
901 g_free ( vdl->gcs );
902
c07088dd
DRP
903 if ( vdl->gcsgradient )
904 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
905 g_object_unref ( vdl->gcsgradient[i] );
906 g_free ( vdl->gcsgradient );
907
ad0a8c2d
EB
908 a_dems_list_free ( vdl->files );
909}
910
0c025cfa 911VikDEMLayer *dem_layer_create ( VikViewport *vp )
ad0a8c2d 912{
0c025cfa 913 VikDEMLayer *vdl = dem_layer_new ( vp );
fa86f1c0 914 gint i;
8aff54f2
RN
915 if ( vp ) {
916 /* TODO: share GCS between layers */
a7023a1b
RN
917 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ ) {
918 if ( i > 0 )
919 vdl->gcs[i] = vik_viewport_new_gc ( vp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
920 }
8aff54f2
RN
921 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
922 vdl->gcsgradient[i] = vik_viewport_new_gc ( vp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
923 }
ad0a8c2d
EB
924 return vdl;
925}
8c721f83
EB
926/**************************************************************
927 **** SOURCES & DOWNLOADING
928 **************************************************************/
929typedef struct {
930 gchar *dest;
931 gdouble lat, lon;
932
933 GMutex *mutex;
934 VikDEMLayer *vdl; /* NULL if not alive */
935
936 guint source;
937} DEMDownloadParams;
938
ad0a8c2d 939
0c1044e9 940/**************************************************
8c721f83
EB
941 * SOURCE: SRTM *
942 **************************************************/
943
944static void srtm_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
0c1044e9 945{
8c721f83 946 gint intlat, intlon;
0e25c0d0 947 const gchar *continent_dir;
8c721f83
EB
948
949 intlat = (int)floor(p->lat);
950 intlon = (int)floor(p->lon);
0e25c0d0
QT
951 continent_dir = srtm_continent_dir(intlat, intlon);
952
953 if (!continent_dir) {
1a272ee7
RN
954 if ( p->vdl ) {
955 gchar *msg = g_strdup_printf ( _("No SRTM data available for %f, %f"), p->lat, p->lon );
956 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(p->vdl), msg, VIK_STATUSBAR_INFO );
957 g_free ( msg );
958 }
0e25c0d0
QT
959 return;
960 }
961
936f4921 962 gchar *src_url = g_strdup_printf("%s/%s/%c%02d%c%03d.hgt.zip",
6308aeed 963 base_url,
0e25c0d0 964 continent_dir,
8c721f83
EB
965 (intlat >= 0) ? 'N' : 'S',
966 ABS(intlat),
967 (intlon >= 0) ? 'E' : 'W',
968 ABS(intlon) );
969
31311c3b 970 static DownloadFileOptions options = { FALSE, FALSE, NULL, 5, a_check_map_file, NULL, NULL };
936f4921 971 DownloadResult_t result = a_http_download_get_url ( src_url, NULL, p->dest, &options, NULL );
4e815e90 972 switch ( result ) {
4dc72a1d 973 case DOWNLOAD_PARAMETERS_ERROR:
4e815e90
RN
974 case DOWNLOAD_CONTENT_ERROR:
975 case DOWNLOAD_HTTP_ERROR: {
976 gchar *msg = g_strdup_printf ( _("DEM download failure for %f, %f"), p->lat, p->lon );
977 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(p->vdl), msg, VIK_STATUSBAR_INFO );
978 g_free ( msg );
979 break;
980 }
981 case DOWNLOAD_FILE_WRITE_ERROR: {
982 gchar *msg = g_strdup_printf ( _("DEM write failure for %s"), p->dest );
983 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(p->vdl), msg, VIK_STATUSBAR_INFO );
984 g_free ( msg );
985 break;
986 }
987 case DOWNLOAD_SUCCESS:
988 case DOWNLOAD_NOT_REQUIRED:
989 default:
990 break;
991 }
936f4921 992 g_free ( src_url );
0c1044e9
EB
993}
994
8c721f83 995static gchar *srtm_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
0c1044e9 996{
8c721f83 997 gint intlat, intlon;
0e25c0d0
QT
998 const gchar *continent_dir;
999
8c721f83
EB
1000 intlat = (int)floor(lat);
1001 intlon = (int)floor(lon);
0e25c0d0
QT
1002 continent_dir = srtm_continent_dir(intlat, intlon);
1003
1004 if (!continent_dir)
1005 continent_dir = "nowhere";
1006
1007 return g_strdup_printf("srtm3-%s%s%c%02d%c%03d.hgt.zip",
1008 continent_dir,
1009 G_DIR_SEPARATOR_S,
8c721f83
EB
1010 (intlat >= 0) ? 'N' : 'S',
1011 ABS(intlat),
1012 (intlon >= 0) ? 'E' : 'W',
1013 ABS(intlon) );
0c1044e9 1014
0c1044e9
EB
1015}
1016
8c721f83
EB
1017/* TODO: generalize */
1018static void srtm_draw_existence ( VikViewport *vp )
0c1044e9 1019{
8c721f83
EB
1020 gdouble max_lat, max_lon, min_lat, min_lon;
1021 gchar buf[strlen(MAPS_CACHE_DIR)+strlen(SRTM_CACHE_TEMPLATE)+30];
1022 gint i, j;
1023
1024 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
1025
1026 for (i = floor(min_lat); i <= floor(max_lat); i++) {
1027 for (j = floor(min_lon); j <= floor(max_lon); j++) {
0e25c0d0
QT
1028 const gchar *continent_dir;
1029 if ((continent_dir = srtm_continent_dir(i, j)) == NULL)
1030 continue;
8c721f83
EB
1031 g_snprintf(buf, sizeof(buf), SRTM_CACHE_TEMPLATE,
1032 MAPS_CACHE_DIR,
0e25c0d0
QT
1033 continent_dir,
1034 G_DIR_SEPARATOR_S,
8c721f83
EB
1035 (i >= 0) ? 'N' : 'S',
1036 ABS(i),
1037 (j >= 0) ? 'E' : 'W',
1038 ABS(j) );
45acf79e 1039 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
8c721f83
EB
1040 VikCoord ne, sw;
1041 gint x1, y1, x2, y2;
1042 sw.north_south = i;
1043 sw.east_west = j;
1044 sw.mode = VIK_COORD_LATLON;
1045 ne.north_south = i+1;
1046 ne.east_west = j+1;
1047 ne.mode = VIK_COORD_LATLON;
1048 vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
1049 vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
1050 if ( x1 < 0 ) x1 = 0;
1051 if ( y2 < 0 ) y2 = 0;
ff37db21 1052 vik_viewport_draw_rectangle ( vp, gtk_widget_get_style(GTK_WIDGET(vp))->black_gc,
8c721f83
EB
1053 FALSE, x1, y2, x2-x1, y1-y2 );
1054 }
1055 }
1056 }
0c1044e9
EB
1057}
1058
8c721f83
EB
1059
1060/**************************************************
1061 * SOURCE: USGS 24K *
1062 **************************************************/
1063
1064#ifdef VIK_CONFIG_DEM24K
1065
1066static void dem24k_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
0c1044e9 1067{
8c721f83
EB
1068 /* TODO: dest dir */
1069 gchar *cmdline = g_strdup_printf("%s %.03f %.03f",
1070 DEM24K_DOWNLOAD_SCRIPT,
1071 floor(p->lat*8)/8,
1072 ceil(p->lon*8)/8 );
1073 /* FIX: don't use system, use execv or something. check for existence */
1074 system(cmdline);
d94f70cc 1075 g_free ( cmdline );
8c721f83 1076}
0c1044e9 1077
8c721f83
EB
1078static gchar *dem24k_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
1079{
1080 return g_strdup_printf("dem24k/%d/%d/%.03f,%.03f.dem",
1081 (gint) lat,
1082 (gint) lon,
1083 floor(lat*8)/8,
1084 ceil(lon*8)/8);
1085}
0c1044e9 1086
8c721f83
EB
1087/* TODO: generalize */
1088static void dem24k_draw_existence ( VikViewport *vp )
1089{
1090 gdouble max_lat, max_lon, min_lat, min_lon;
1091 gchar buf[strlen(MAPS_CACHE_DIR)+40];
1092 gdouble i, j;
0c1044e9 1093
8c721f83 1094 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
0c1044e9 1095
8c721f83
EB
1096 for (i = floor(min_lat*8)/8; i <= floor(max_lat*8)/8; i+=0.125) {
1097 /* check lat dir first -- faster */
1098 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/",
1099 MAPS_CACHE_DIR,
1100 (gint) i );
6b1a58ec 1101 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
8c721f83
EB
1102 continue;
1103 for (j = floor(min_lon*8)/8; j <= floor(max_lon*8)/8; j+=0.125) {
1104 /* check lon dir first -- faster */
1105 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/",
1106 MAPS_CACHE_DIR,
1107 (gint) i,
1108 (gint) j );
45acf79e 1109 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
8c721f83
EB
1110 continue;
1111 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/%.03f,%.03f.dem",
1112 MAPS_CACHE_DIR,
1113 (gint) i,
1114 (gint) j,
1115 floor(i*8)/8,
1116 floor(j*8)/8 );
45acf79e 1117 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
8c721f83
EB
1118 VikCoord ne, sw;
1119 gint x1, y1, x2, y2;
1120 sw.north_south = i;
1121 sw.east_west = j-0.125;
1122 sw.mode = VIK_COORD_LATLON;
1123 ne.north_south = i+0.125;
1124 ne.east_west = j;
1125 ne.mode = VIK_COORD_LATLON;
1126 vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
1127 vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
1128 if ( x1 < 0 ) x1 = 0;
1129 if ( y2 < 0 ) y2 = 0;
ff37db21 1130 vik_viewport_draw_rectangle ( vp, gtk_widget_get_style(GTK_WIDGET(vp))->black_gc,
8c721f83
EB
1131 FALSE, x1, y2, x2-x1, y1-y2 );
1132 }
1133 }
1134 }
1135}
1136#endif
0c1044e9 1137
8c721f83
EB
1138/**************************************************
1139 * SOURCES -- DOWNLOADING & IMPORTING TOOL *
1140 **************************************************
1141 */
0c1044e9 1142
8c721f83
EB
1143static void weak_ref_cb ( gpointer ptr, GObject * dead_vdl )
1144{
1145 DEMDownloadParams *p = ptr;
1146 g_mutex_lock ( p->mutex );
1147 p->vdl = NULL;
1148 g_mutex_unlock ( p->mutex );
1149}
0c1044e9 1150
8c721f83 1151/* Try to add file full_path.
88542aa9 1152 * filename will be copied.
8c721f83
EB
1153 * returns FALSE if file does not exists, TRUE otherwise.
1154 */
88542aa9 1155static gboolean dem_layer_add_file ( VikDEMLayer *vdl, const gchar *filename )
8c721f83 1156{
88542aa9 1157 if ( g_file_test(filename, G_FILE_TEST_EXISTS) == TRUE ) {
b01c3429 1158 /* only load if file size is not 0 (not in progress) */
29fb7801 1159 GStatBuf sb;
603b6b7e 1160 (void)g_stat ( filename, &sb );
0c1044e9 1161 if ( sb.st_size ) {
88542aa9 1162 gchar *duped_path = g_strdup(filename);
8c721f83
EB
1163 vdl->files = g_list_prepend ( vdl->files, duped_path );
1164 a_dems_load ( duped_path );
b01c3429 1165 g_debug("%s: %s", __FUNCTION__, duped_path);
0c1044e9 1166 }
8c721f83
EB
1167 return TRUE;
1168 } else
1169 return FALSE;
1170}
1171
1172static void dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
1173{
1174 if ( p->source == DEM_SOURCE_SRTM )
1175 srtm_dem_download_thread ( p, threaddata );
1176#ifdef VIK_CONFIG_DEM24K
1177 else if ( p->source == DEM_SOURCE_DEM24K )
1178 dem24k_dem_download_thread ( p, threaddata );
1179#endif
062ad57c
RN
1180 else
1181 return;
8c721f83 1182
8c721f83
EB
1183 g_mutex_lock ( p->mutex );
1184 if ( p->vdl ) {
1185 g_object_weak_unref ( G_OBJECT(p->vdl), weak_ref_cb, p );
1186
1187 if ( dem_layer_add_file ( p->vdl, p->dest ) )
d3b7baa7 1188 vik_layer_emit_update ( VIK_LAYER(p->vdl) ); // NB update requested from background thread
8c721f83
EB
1189 }
1190 g_mutex_unlock ( p->mutex );
8c721f83
EB
1191}
1192
1193
1194static void free_dem_download_params ( DEMDownloadParams *p )
1195{
fc6640a9 1196 vik_mutex_free ( p->mutex );
8c721f83
EB
1197 g_free ( p->dest );
1198 g_free ( p );
1199}
1200
1201static gpointer dem_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1202{
1203 return vvp;
1204}
1205
13d3fc89
RN
1206/**
1207 * Display a simple dialog with information about the DEM file at this location
1208 */
1209static void dem_layer_file_info ( GtkWidget *widget, struct LatLon *ll )
1210{
1211 gint intlat, intlon;
1212 const gchar *continent_dir;
1213
1214 intlat = (int)floor(ll->lat);
1215 intlon = (int)floor(ll->lon);
1216 continent_dir = srtm_continent_dir(intlat, intlon);
1217
1218 gchar *source = NULL;
1219 if ( continent_dir )
936f4921 1220 source = g_strdup_printf ( "%s/%s/%c%02d%c%03d.hgt.zip",
6308aeed 1221 base_url,
13d3fc89
RN
1222 continent_dir,
1223 (intlat >= 0) ? 'N' : 'S',
1224 ABS(intlat),
1225 (intlon >= 0) ? 'E' : 'W',
1226 ABS(intlon) );
1227 else
1228 // Probably not over any land...
1229 source = g_strdup ( _("No DEM File Available") );
1230
1231 gchar *filename = NULL;
1232 gchar *dem_file = NULL;
1233#ifdef VIK_CONFIG_DEM24K
1234 dem_file = dem24k_lat_lon_to_dest_fn ( ll->lat, ll->lon );
1235#else
1236 dem_file = srtm_lat_lon_to_dest_fn ( ll->lat, ll->lon );
1237#endif
1238 gchar *message = NULL;
1239
1240 filename = g_strdup_printf ( "%s%s", MAPS_CACHE_DIR, dem_file );
1241
1242 if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1243 // Get some timestamp information of the file
2a708810 1244 GStatBuf stat_buf;
13d3fc89
RN
1245 if ( g_stat ( filename, &stat_buf ) == 0 ) {
1246 gchar time_buf[64];
1247 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
1248 message = g_strdup_printf ( _("\nSource: %s\n\nDEM File: %s\nDEM File Timestamp: %s"), source, filename, time_buf );
1249 }
1250 }
1251 else
1252 message = g_strdup_printf ( _("Source: %s\n\nNo DEM File!"), source );
1253
1254 // Show the info
1255 a_dialog_info_msg ( GTK_WINDOW(gtk_widget_get_toplevel(widget)), message );
1256
1257 g_free ( message );
1258 g_free ( source );
1259 g_free ( dem_file );
1260 g_free ( filename );
1261}
8c721f83
EB
1262
1263static gboolean dem_layer_download_release ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1264{
1265 VikCoord coord;
13d3fc89 1266 static struct LatLon ll;
8c721f83
EB
1267
1268 gchar *full_path;
1269 gchar *dem_file = NULL;
1270
8c721f83
EB
1271 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1272 vik_coord_to_latlon ( &coord, &ll );
1273
1274
1275 if ( vdl->source == DEM_SOURCE_SRTM )
1276 dem_file = srtm_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1277#ifdef VIK_CONFIG_DEM24K
1278 else if ( vdl->source == DEM_SOURCE_DEM24K )
1279 dem_file = dem24k_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1280#endif
1281
1282 if ( ! dem_file )
1283 return TRUE;
1284
1285 full_path = g_strdup_printf("%s%s", MAPS_CACHE_DIR, dem_file );
1286
b01c3429 1287 g_debug("%s: %s", __FUNCTION__, full_path);
8c721f83 1288
13d3fc89
RN
1289 if ( event->button == 1 ) {
1290 // TODO: check if already in filelist
1291 if ( ! dem_layer_add_file(vdl, full_path) ) {
1292 gchar *tmp = g_strdup_printf ( _("Downloading DEM %s"), dem_file );
1293 DEMDownloadParams *p = g_malloc(sizeof(DEMDownloadParams));
1294 p->dest = g_strdup(full_path);
1295 p->lat = ll.lat;
1296 p->lon = ll.lon;
1297 p->vdl = vdl;
fc6640a9 1298 p->mutex = vik_mutex_new();
13d3fc89
RN
1299 p->source = vdl->source;
1300 g_object_weak_ref(G_OBJECT(p->vdl), weak_ref_cb, p );
1301
c75da936
RN
1302 a_background_thread ( BACKGROUND_POOL_REMOTE,
1303 VIK_GTK_WINDOW_FROM_LAYER(vdl), tmp,
13d3fc89
RN
1304 (vik_thr_func) dem_download_thread, p,
1305 (vik_thr_free_func) free_dem_download_params, NULL, 1 );
1306
1307 g_free ( tmp );
1308 }
1309 else
1310 vik_layer_emit_update ( VIK_LAYER(vdl) );
1311 }
1312 else {
1313 if ( !vdl->right_click_menu ) {
1314 GtkWidget *item;
1315 vdl->right_click_menu = GTK_MENU ( gtk_menu_new () );
1316
1317 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show DEM File Information") );
1318 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
1319 g_signal_connect ( G_OBJECT(item), "activate", G_CALLBACK(dem_layer_file_info), &ll );
1320 gtk_menu_shell_append (GTK_MENU_SHELL(vdl->right_click_menu), item);
1321 }
2ca82974 1322
13d3fc89
RN
1323 gtk_menu_popup ( vdl->right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1324 gtk_widget_show_all ( GTK_WIDGET(vdl->right_click_menu) );
0c1044e9
EB
1325 }
1326
8c721f83
EB
1327 g_free ( dem_file );
1328 g_free ( full_path );
1329
0c1044e9
EB
1330 return TRUE;
1331}
1332
1333static gboolean dem_layer_download_click ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1334{
1335/* choose & keep track of cache dir
1336 * download in background thread
1337 * download over area */
1338 return TRUE;
1339}
1340
1341