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