2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
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.
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.
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
28 #ifdef HAVE_SYS_TYPES_H
29 #include <sys/types.h>
31 #ifdef HAVE_SYS_STAT_H
38 #include <glib/gstdio.h>
39 #include <glib/gi18n.h>
41 #include "background.h"
43 #include "vikmapslayer.h"
44 #include "vikdemlayer.h"
47 #include "icons/icons.h"
49 #define MAPS_CACHE_DIR maps_layer_default_dir()
50 #define SRTM_CACHE_TEMPLATE "%ssrtm3-%s%s%c%02d%c%03d.hgt.zip"
52 #define SRTM_HTTP_BASE_URL "https://dds.cr.usgs.gov/srtm/version2_1/SRTM3"
53 static gchar *base_url = NULL;
54 #define VIK_SETTINGS_SRTM_HTTP_BASE_URL "srtm_http_base_url"
56 #ifdef VIK_CONFIG_DEM24K
57 #define DEM24K_DOWNLOAD_SCRIPT "dem24k.pl"
60 #define UNUSED_LINE_THICKNESS 3
62 static VikDEMLayer *dem_layer_new ( VikViewport *vvp );
63 static void dem_layer_draw ( VikDEMLayer *vdl, VikViewport *vp );
64 static void dem_layer_free ( VikDEMLayer *vdl );
65 static VikDEMLayer *dem_layer_create ( VikViewport *vp );
66 static const gchar* dem_layer_tooltip( VikDEMLayer *vdl );
67 static void dem_layer_marshall( VikDEMLayer *vdl, guint8 **data, gint *len );
68 static VikDEMLayer *dem_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
69 static gboolean dem_layer_set_param ( VikDEMLayer *vdl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
70 static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gboolean is_file_operation );
71 static void dem_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file );
72 static void srtm_draw_existence ( VikViewport *vp );
74 #ifdef VIK_CONFIG_DEM24K
75 static void dem24k_draw_existence ( VikViewport *vp );
78 /* Upped upper limit incase units are feet */
79 static VikLayerParamScale param_scales[] = {
84 static gchar *params_source[] = {
85 N_("SRTM Global 90m (3 arcsec)"),
86 #ifdef VIK_CONFIG_DEM24K
92 static gchar *params_type[] = {
93 N_("Absolute height"),
94 N_("Height gradient"),
98 enum { DEM_SOURCE_SRTM,
99 #ifdef VIK_CONFIG_DEM24K
104 enum { DEM_TYPE_HEIGHT = 0,
109 static VikLayerParamData color_default ( void ) {
110 VikLayerParamData data; gdk_color_parse ( "blue", &data.c ); return data;
113 static VikLayerParamData source_default ( void ) { return VIK_LPD_UINT ( DEM_SOURCE_SRTM ); }
114 static VikLayerParamData type_default ( void ) { return VIK_LPD_UINT ( DEM_TYPE_HEIGHT ); }
115 static VikLayerParamData min_elev_default ( void ) { return VIK_LPD_DOUBLE ( 0.0 ); }
116 static VikLayerParamData max_elev_default ( void ) { return VIK_LPD_DOUBLE ( 1000.0 ); }
118 static VikLayerParam dem_layer_params[] = {
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 },
128 enum { PARAM_FILES=0, PARAM_SOURCE, PARAM_COLOR, PARAM_TYPE, PARAM_MIN_ELEV, PARAM_MAX_ELEV, NUM_PARAMS };
130 static gpointer dem_layer_download_create ( VikWindow *vw, VikViewport *vvp);
131 static gboolean dem_layer_download_release ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp );
132 static gboolean dem_layer_download_click ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp );
134 static VikToolInterface dem_tools[] = {
135 { { "DEMDownload", "vik-icon-DEM Download", N_("_DEM Download"), NULL, N_("DEM Download"), 0 },
136 (VikToolConstructorFunc) dem_layer_download_create, NULL, NULL, NULL,
137 (VikToolMouseFunc) dem_layer_download_click, NULL, (VikToolMouseFunc) dem_layer_download_release,
138 (VikToolKeyFunc) NULL,
140 GDK_CURSOR_IS_PIXMAP, &cursor_demdl_pixbuf, NULL },
145 The first entry is blue for a default 'sea' colour,
146 however the value used by the corresponding gc can be configured as part of the DEM layer properties.
147 The other colours, shaded from brown to white are used to give an indication of height.
149 static gchar *dem_height_colors[] = {
151 "#9b793c", "#9c7d40", "#9d8144", "#9e8549", "#9f894d", "#a08d51", "#a29156", "#a3955a", "#a4995e", "#a69d63",
152 "#a89f65", "#aaa267", "#ada569", "#afa76b", "#b1aa6d", "#b4ad6f", "#b6b071", "#b9b373", "#bcb676", "#beb978",
153 "#c0bc7a", "#c2c07d", "#c4c37f", "#c6c681", "#c8ca84", "#cacd86", "#ccd188", "#cfd58b", "#c2ce84", "#b5c87e",
154 "#a9c278", "#9cbb71", "#8fb56b", "#83af65", "#76a95e", "#6aa358", "#5e9d52", "#63a055", "#69a458", "#6fa85c",
155 "#74ac5f", "#7ab063", "#80b467", "#86b86a", "#8cbc6e", "#92c072", "#94c175", "#97c278", "#9ac47c", "#9cc57f",
156 "#9fc682", "#a2c886", "#a4c989", "#a7cb8d", "#aacd91", "#afce99", "#b5d0a1", "#bbd2aa", "#c0d3b2", "#c6d5ba",
157 "#ccd7c3", "#d1d9cb", "#d7dbd4", "#DDDDDD", "#e0e0e0", "#e4e4e4", "#e8e8e8", "#ebebeb", "#efefef", "#f3f3f3",
158 "#f7f7f7", "#fbfbfb", "#ffffff"
161 static const guint DEM_N_HEIGHT_COLORS = sizeof(dem_height_colors)/sizeof(dem_height_colors[0]);
164 "#9b793c", "#9e8549", "#a29156", "#a69d63", "#ada569", "#b4ad6f", "#bcb676", "#c2c07d", "#c8ca84", "#cfd58b",
165 "#a9c278", "#83af65", "#5e9d52", "#6fa85c", "#80b467", "#92c072", "#9ac47c", "#a2c886", "#aacd91", "#bbd2aa",
166 "#ccd7c3", "#DDDDDD", "#e8e8e8", "#f3f3f3", "#FFFFFF"
170 static gchar *dem_gradient_colors[] = {
172 "#000000", "#000011", "#000022", "#000033", "#000044", "#00004c", "#000055", "#00005d", "#000066", "#00006e",
173 "#000077", "#00007f", "#000088", "#000090", "#000099", "#0000a1", "#0000aa", "#0000b2", "#0000bb", "#0000c3",
174 "#0000cc", "#0000d4", "#0000dd", "#0000e5", "#0000ee", "#0000f6", "#0000ff", "#0008f7", "#0011ee", "#0019e6",
175 "#0022dd", "#002ad5", "#0033cc", "#003bc4", "#0044bb", "#004cb3", "#0055aa", "#005da2", "#006699", "#006e91",
176 "#007788", "#007f80", "#008877", "#00906f", "#009966", "#00a15e", "#00aa55", "#00b24d", "#00bb44", "#00c33c",
177 "#00cc33", "#00d42b", "#00dd22", "#00e51a", "#00ee11", "#00f609", "#00ff00", "#08f700", "#11ee00", "#19e600",
178 "#22dd00", "#2ad500", "#33cc00", "#3bc400", "#44bb00", "#4cb300", "#55aa00", "#5da200", "#669900", "#6e9100",
179 "#778800", "#7f8000", "#887700", "#906f00", "#996600", "#a15e00", "#aa5500", "#b24d00", "#bb4400", "#c33c00",
180 "#cc3300", "#d42b00", "#dd2200", "#e51a00", "#ee1100", "#f60900", "#ff0000",
184 static const guint DEM_N_GRADIENT_COLORS = sizeof(dem_gradient_colors)/sizeof(dem_gradient_colors[0]);
187 VikLayerInterface vik_dem_layer_interface = {
194 sizeof(dem_tools) / sizeof(dem_tools[0]),
203 (VikLayerFuncCreate) dem_layer_create,
204 (VikLayerFuncRealize) NULL,
205 (VikLayerFuncPostRead) dem_layer_post_read,
206 (VikLayerFuncFree) dem_layer_free,
208 (VikLayerFuncProperties) NULL,
209 (VikLayerFuncDraw) dem_layer_draw,
210 (VikLayerFuncChangeCoordMode) NULL,
212 (VikLayerFuncGetTimestamp) NULL,
214 (VikLayerFuncSetMenuItemsSelection) NULL,
215 (VikLayerFuncGetMenuItemsSelection) NULL,
217 (VikLayerFuncAddMenuItems) NULL,
218 (VikLayerFuncSublayerAddMenuItems) NULL,
220 (VikLayerFuncSublayerRenameRequest) NULL,
221 (VikLayerFuncSublayerToggleVisible) NULL,
222 (VikLayerFuncSublayerTooltip) NULL,
223 (VikLayerFuncLayerTooltip) dem_layer_tooltip,
224 (VikLayerFuncLayerSelected) NULL,
226 (VikLayerFuncMarshall) dem_layer_marshall,
227 (VikLayerFuncUnmarshall) dem_layer_unmarshall,
229 (VikLayerFuncSetParam) dem_layer_set_param,
230 (VikLayerFuncGetParam) dem_layer_get_param,
231 (VikLayerFuncChangeParam) NULL,
233 (VikLayerFuncReadFileData) NULL,
234 (VikLayerFuncWriteFileData) NULL,
236 (VikLayerFuncDeleteItem) NULL,
237 (VikLayerFuncCutItem) NULL,
238 (VikLayerFuncCopyItem) NULL,
239 (VikLayerFuncPasteItem) NULL,
240 (VikLayerFuncFreeCopiedItem) NULL,
241 (VikLayerFuncDragDropRequest) NULL,
243 (VikLayerFuncSelectClick) NULL,
244 (VikLayerFuncSelectMove) NULL,
245 (VikLayerFuncSelectRelease) NULL,
246 (VikLayerFuncSelectedViewportMenu) NULL,
249 struct _VikDEMLayer {
260 // right click menu only stuff - similar to mapslayer
261 GtkMenu *right_click_menu;
264 // NB Only performed once per program run
265 static void vik_dem_class_init ( VikDEMLayerClass *klass )
267 // Note if suppling your own base URL - the site must still follow the Continent directory layout
268 if ( ! a_settings_get_string ( VIK_SETTINGS_SRTM_HTTP_BASE_URL, &base_url ) ) {
269 // Otherwise use the default
270 base_url = g_strdup ( SRTM_HTTP_BASE_URL );
274 GType vik_dem_layer_get_type ()
276 static GType vdl_type = 0;
280 static const GTypeInfo vdl_info =
282 sizeof (VikDEMLayerClass),
283 NULL, /* base_init */
284 NULL, /* base_finalize */
285 (GClassInitFunc) vik_dem_class_init, /* class init */
286 NULL, /* class_finalize */
287 NULL, /* class_data */
288 sizeof (VikDEMLayer),
290 NULL /* instance init */
292 vdl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikDEMLayer", &vdl_info, 0 );
298 static const gchar* dem_layer_tooltip( VikDEMLayer *vdl )
300 static gchar tmp_buf[100];
301 g_snprintf (tmp_buf, sizeof(tmp_buf), _("Number of files: %d"), g_list_length (vdl->files));
305 static void dem_layer_marshall( VikDEMLayer *vdl, guint8 **data, gint *len )
307 vik_layer_marshall_params ( VIK_LAYER(vdl), data, len );
310 static VikDEMLayer *dem_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
312 VikDEMLayer *rv = dem_layer_new ( vvp );
315 /* TODO: share GCS between layers */
316 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ ) {
318 rv->gcs[i] = vik_viewport_new_gc_from_color ( vvp, &(rv->color), UNUSED_LINE_THICKNESS );
320 rv->gcs[i] = vik_viewport_new_gc ( vvp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
322 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
323 rv->gcsgradient[i] = vik_viewport_new_gc ( vvp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
325 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
329 /* Structure for DEM data used in background thread */
332 } dem_load_thread_data;
335 * Function for starting the DEM file loading as a background thread
337 static int dem_layer_load_list_thread ( dem_load_thread_data *dltd, gpointer threaddata )
339 int result = 0; // Default to good
341 if ( a_dems_load_list ( &(dltd->vdl->files), threaddata ) ) {
346 // ATM as each file is processed the screen is not updated (no mechanism exposed to a_dems_load_list)
347 // Thus force draw only at the end, as loading is complete/aborted
348 //gdk_threads_enter();
349 // Test is helpful to prevent Gtk-CRITICAL warnings if the program is exitted whilst loading
350 if ( IS_VIK_LAYER(dltd->vdl) )
351 vik_layer_emit_update ( VIK_LAYER(dltd->vdl) ); // NB update from background thread
352 //gdk_threads_leave();
357 static void dem_layer_thread_data_free ( dem_load_thread_data *data )
363 static void dem_layer_thread_cancel ( dem_load_thread_data *data )
366 // Instead of freeing the list, leave it as partially processed
367 // Thus we can see/use what was done
371 * Process the list of DEM files and convert each one to a relative path
373 static GList *dem_layer_convert_to_relative_filenaming ( GList *files )
375 gchar *cwd = g_get_current_dir();
379 GList *relfiles = NULL;
382 gchar *file = g_strdup ( file_GetRelativeFilename ( cwd, files->data ) );
383 relfiles = g_list_prepend ( relfiles, file );
390 // Replacing current list, so delete old values first.
393 g_free ( iter->data );
396 g_list_free ( files );
404 gboolean dem_layer_set_param ( VikDEMLayer *vdl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
408 case PARAM_COLOR: vdl->color = data.c; gdk_gc_set_rgb_fg_color ( vdl->gcs[0], &(vdl->color) ); break;
409 case PARAM_SOURCE: vdl->source = data.u; break;
410 case PARAM_TYPE: vdl->type = data.u; break;
412 /* Convert to store internally
413 NB file operation always in internal units (metres) */
414 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
415 vdl->min_elev = VIK_FEET_TO_METERS(data.d);
417 vdl->min_elev = data.d;
420 /* Convert to store internally
421 NB file operation always in internal units (metres) */
422 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
423 vdl->max_elev = VIK_FEET_TO_METERS(data.d);
425 vdl->max_elev = data.d;
429 // Clear out old settings - if any commonalities with new settings they will have to be read again
430 a_dems_list_free ( vdl->files );
431 // Set file list so any other intermediate screen drawing updates will show currently loaded DEMs by the working thread
432 vdl->files = data.sl;
433 // No need for thread if no files
436 dem_load_thread_data *dltd = g_malloc ( sizeof(dem_load_thread_data) );
438 dltd->vdl->files = data.sl;
440 a_background_thread ( BACKGROUND_POOL_LOCAL,
441 VIK_GTK_WINDOW_FROM_WIDGET(vp),
443 (vik_thr_func) dem_layer_load_list_thread,
445 (vik_thr_free_func) dem_layer_thread_data_free,
446 (vik_thr_free_func) dem_layer_thread_cancel,
447 g_list_length ( data.sl ) ); // Number of DEM files
456 static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gboolean is_file_operation )
458 VikLayerParamData rv;
463 if ( is_file_operation )
464 // Save in relative format if necessary
465 if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE )
466 rv.sl = dem_layer_convert_to_relative_filenaming ( rv.sl );
468 case PARAM_SOURCE: rv.u = vdl->source; break;
469 case PARAM_TYPE: rv.u = vdl->type; break;
470 case PARAM_COLOR: rv.c = vdl->color; break;
472 /* Convert for display in desired units
473 NB file operation always in internal units (metres) */
474 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
475 rv.d = VIK_METERS_TO_FEET(vdl->min_elev);
477 rv.d = vdl->min_elev;
480 /* Convert for display in desired units
481 NB file operation always in internal units (metres) */
482 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
483 rv.d = VIK_METERS_TO_FEET(vdl->max_elev);
485 rv.d = vdl->max_elev;
492 static void dem_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file )
494 /* nothing ATM, but keep in case it's needed the future */
497 static VikDEMLayer *dem_layer_new ( VikViewport *vvp )
499 VikDEMLayer *vdl = VIK_DEM_LAYER ( g_object_new ( VIK_DEM_LAYER_TYPE, NULL ) );
501 vik_layer_set_type ( VIK_LAYER(vdl), VIK_LAYER_DEM );
505 vdl->gcs = g_malloc(sizeof(GdkGC *)*DEM_N_HEIGHT_COLORS);
506 vdl->gcsgradient = g_malloc(sizeof(GdkGC *)*DEM_N_GRADIENT_COLORS);
507 /* make new gcs only if we need it (copy layer -> use old) */
509 // Ensure the base GC is available so the default colour can be applied
510 if ( vvp ) vdl->gcs[0] = vik_viewport_new_gc ( vvp, "#0000FF", 1 );
512 vik_layer_set_defaults ( VIK_LAYER(vdl), vvp );
518 static inline guint16 get_height_difference(gint16 elev, gint16 new_elev)
520 if(new_elev == VIK_DEM_INVALID_ELEVATION)
523 return abs(new_elev - elev);
527 static void vik_dem_layer_draw_dem ( VikDEMLayer *vdl, VikViewport *vp, VikDEM *dem )
529 VikDEMColumn *column, *prevcolumn, *nextcolumn;
531 struct LatLon dem_northeast, dem_southwest;
532 gdouble max_lat, max_lon, min_lat, min_lon;
534 /**** Check if viewport and DEM data overlap ****/
536 /* get min, max lat/lon of viewport */
537 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
539 /* get min, max lat/lon of DEM data */
540 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
541 dem_northeast.lat = dem->max_north / 3600.0;
542 dem_northeast.lon = dem->max_east / 3600.0;
543 dem_southwest.lat = dem->min_north / 3600.0;
544 dem_southwest.lon = dem->min_east / 3600.0;
545 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
546 struct UTM dem_northeast_utm, dem_southwest_utm;
547 dem_northeast_utm.northing = dem->max_north;
548 dem_northeast_utm.easting = dem->max_east;
549 dem_southwest_utm.northing = dem->min_north;
550 dem_southwest_utm.easting = dem->min_east;
551 dem_northeast_utm.zone = dem_southwest_utm.zone = dem->utm_zone;
552 dem_northeast_utm.letter = dem_southwest_utm.letter = dem->utm_letter;
554 a_coords_utm_to_latlon(&dem_northeast_utm, &dem_northeast);
555 a_coords_utm_to_latlon(&dem_southwest_utm, &dem_southwest);
557 // Unknown horiz_units - this shouldn't normally happen
558 // Thus can't work out positions to use
562 if ( (max_lat > dem_northeast.lat && min_lat > dem_northeast.lat) ||
563 (max_lat < dem_southwest.lat && min_lat < dem_southwest.lat) )
565 else if ( (max_lon > dem_northeast.lon && min_lon > dem_northeast.lon) ||
566 (max_lon < dem_southwest.lon && min_lon < dem_southwest.lon) )
568 /* else they overlap */
570 /**** End Overlap Check ****/
571 /* boxes to show where we have DEM instead of actually drawing the DEM.
572 * useful if we want to see what areas we have coverage for (if we want
573 * to get elevation data for a track) but don't want to cover the map.
577 /* draw a box if a DEM is loaded. in future I'd like to add an option for this
578 * this is useful if we want to see what areas we have dem for but don't want to
579 * cover the map (or maybe we just need translucent DEM?) */
581 VikCoord demne, demsw;
583 vik_coord_load_from_latlon(&demne, vik_viewport_get_coord_mode(vp), &dem_northeast);
584 vik_coord_load_from_latlon(&demsw, vik_viewport_get_coord_mode(vp), &dem_southwest);
586 vik_viewport_coord_to_screen ( vp, &demne, &x1, &y1 );
587 vik_viewport_coord_to_screen ( vp, &demsw, &x2, &y2 );
589 if ( x1 > vik_viewport_get_width(vp) ) x1=vik_viewport_get_width(vp);
590 if ( y2 > vik_viewport_get_height(vp) ) y2=vik_viewport_get_height(vp);
591 if ( x2 < 0 ) x2 = 0;
592 if ( y1 < 0 ) y1 = 0;
593 vik_viewport_draw_rectangle ( vp, gtk_widget_get_style(GTK_WIDGET(vp))->black_gc,
594 FALSE, x2, y1, x1-x2, y2-y1 );
599 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
600 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
602 gdouble max_lat_as, max_lon_as, min_lat_as, min_lon_as;
603 gdouble start_lat_as, end_lat_as, start_lon_as, end_lon_as;
605 gdouble start_lat, end_lat, start_lon, end_lon;
607 struct LatLon counter;
609 guint x, y, start_x, start_y;
613 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 80 ); /* todo: smarter calculation. */
615 gdouble nscale_deg = dem->north_scale / ((gdouble) 3600);
616 gdouble escale_deg = dem->east_scale / ((gdouble) 3600);
618 max_lat_as = max_lat * 3600;
619 min_lat_as = min_lat * 3600;
620 max_lon_as = max_lon * 3600;
621 min_lon_as = min_lon * 3600;
623 start_lat_as = MAX(min_lat_as, dem->min_north);
624 end_lat_as = MIN(max_lat_as, dem->max_north);
625 start_lon_as = MAX(min_lon_as, dem->min_east);
626 end_lon_as = MIN(max_lon_as, dem->max_east);
628 start_lat = floor(start_lat_as / dem->north_scale) * nscale_deg;
629 end_lat = ceil (end_lat_as / dem->north_scale) * nscale_deg;
630 start_lon = floor(start_lon_as / dem->east_scale) * escale_deg;
631 end_lon = ceil (end_lon_as / dem->east_scale) * escale_deg;
633 vik_dem_east_north_to_xy ( dem, start_lon_as, start_lat_as, &start_x, &start_y );
634 guint gradient_skip_factor = 1;
635 if(vdl->type == DEM_TYPE_GRADIENT)
636 gradient_skip_factor = skip_factor;
638 /* verify sane elev interval */
639 if ( vdl->max_elev <= vdl->min_elev )
640 vdl->max_elev = vdl->min_elev + 1;
642 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 ) {
643 // NOTE: ( counter.lon <= end_lon + ESCALE_DEG*SKIP_FACTOR ) is neccessary so in high zoom modes,
644 // the leftmost column does also get drawn, if the center point is out of viewport.
645 if ( x < dem->n_columns ) {
646 column = g_ptr_array_index ( dem->columns, x );
647 // get previous and next column. catch out-of-bound.
649 new_x -= gradient_skip_factor;
651 prevcolumn = g_ptr_array_index ( dem->columns, x+1);
653 prevcolumn = g_ptr_array_index ( dem->columns, new_x);
655 new_x += gradient_skip_factor;
656 if(new_x >= dem->n_columns)
657 nextcolumn = g_ptr_array_index ( dem->columns, x-1);
659 nextcolumn = g_ptr_array_index ( dem->columns, new_x);
661 for ( y=start_y, counter.lat = start_lat; counter.lat <= end_lat; counter.lat += nscale_deg * skip_factor, y += skip_factor ) {
662 if ( y > column->n_points )
665 elev = column->points[y];
667 // calculate bounding box for drawing
668 gint box_x, box_y, box_width, box_height;
671 box_c.lat += (nscale_deg * skip_factor)/2;
672 box_c.lon -= (escale_deg * skip_factor)/2;
673 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
674 vik_viewport_coord_to_screen(vp, &tmp, &box_x, &box_y);
675 // catch box at borders
680 box_c.lat -= nscale_deg * skip_factor;
681 box_c.lon += escale_deg * skip_factor;
682 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
683 vik_viewport_coord_to_screen(vp, &tmp, &box_width, &box_height);
686 // catch box at borders
687 if(box_width < 0 || box_height < 0)
688 // skip this as is out of the viewport (e.g. zoomed in so this point is way off screen)
691 gboolean below_minimum = FALSE;
692 if(vdl->type == DEM_TYPE_HEIGHT) {
693 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev ) {
694 // Prevent 'elev - vdl->min_elev' from being negative so can safely use as array index
695 elev = ceil ( vdl->min_elev );
696 below_minimum = TRUE;
698 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
699 elev = vdl->max_elev;
703 if(vdl->type == DEM_TYPE_GRADIENT) {
704 if( elev == VIK_DEM_INVALID_ELEVATION ) {
707 // calculate and sum gradient in all directions
711 // calculate gradient from height points all around the current one
712 new_y = y - gradient_skip_factor;
715 change += get_height_difference(elev, prevcolumn->points[new_y]);
716 change += get_height_difference(elev, column->points[new_y]);
717 change += get_height_difference(elev, nextcolumn->points[new_y]);
719 change += get_height_difference(elev, prevcolumn->points[y]);
720 change += get_height_difference(elev, nextcolumn->points[y]);
722 new_y = y + gradient_skip_factor;
723 if(new_y >= column->n_points)
725 change += get_height_difference(elev, prevcolumn->points[new_y]);
726 change += get_height_difference(elev, column->points[new_y]);
727 change += get_height_difference(elev, nextcolumn->points[new_y]);
729 change = change / ((skip_factor > 1) ? log(skip_factor) : 0.55); // FIXME: better calc.
731 if(change < vdl->min_elev)
732 // Prevent 'change - vdl->min_elev' from being negative so can safely use as array index
733 change = ceil ( vdl->min_elev );
735 if(change > vdl->max_elev)
736 change = vdl->max_elev;
738 // void vik_viewport_draw_rectangle ( VikViewport *vvp, GdkGC *gc, gboolean filled, gint x1, gint y1, gint x2, gint y2 );
739 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);
742 if(vdl->type == DEM_TYPE_HEIGHT) {
743 if ( elev == VIK_DEM_INVALID_ELEVATION )
744 ; /* don't draw it */
745 else if ( elev <= 0 || below_minimum )
746 /* If 'sea' colour or below the defined mininum draw in the configurable colour */
747 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, box_x, box_y, box_width, box_height);
749 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);
756 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
757 gdouble max_nor, max_eas, min_nor, min_eas;
758 gdouble start_nor, start_eas, end_nor, end_eas;
762 guint x, y, start_x, start_y;
764 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
767 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 10 ); /* todo: smarter calculation. */
769 VikCoord tleft, tright, bleft, bright;
771 vik_viewport_screen_to_coord ( vp, 0, 0, &tleft );
772 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), 0, &tright );
773 vik_viewport_screen_to_coord ( vp, 0, vik_viewport_get_height(vp), &bleft );
774 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), vik_viewport_get_height(vp), &bright );
777 vik_coord_convert(&tleft, VIK_COORD_UTM);
778 vik_coord_convert(&tright, VIK_COORD_UTM);
779 vik_coord_convert(&bleft, VIK_COORD_UTM);
780 vik_coord_convert(&bright, VIK_COORD_UTM);
782 max_nor = MAX(tleft.north_south, tright.north_south);
783 min_nor = MIN(bleft.north_south, bright.north_south);
784 max_eas = MAX(bright.east_west, tright.east_west);
785 min_eas = MIN(bleft.east_west, tleft.east_west);
787 start_nor = MAX(min_nor, dem->min_north);
788 end_nor = MIN(max_nor, dem->max_north);
789 if ( tleft.utm_zone == dem->utm_zone && bleft.utm_zone == dem->utm_zone
790 && (tleft.utm_letter >= 'N') == (dem->utm_letter >= 'N')
791 && (bleft.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
792 start_eas = MAX(min_eas, dem->min_east);
794 start_eas = dem->min_east;
795 if ( tright.utm_zone == dem->utm_zone && bright.utm_zone == dem->utm_zone
796 && (tright.utm_letter >= 'N') == (dem->utm_letter >= 'N')
797 && (bright.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
798 end_eas = MIN(max_eas, dem->max_east);
800 end_eas = dem->max_east;
802 start_nor = floor(start_nor / dem->north_scale) * dem->north_scale;
803 end_nor = ceil (end_nor / dem->north_scale) * dem->north_scale;
804 start_eas = floor(start_eas / dem->east_scale) * dem->east_scale;
805 end_eas = ceil (end_eas / dem->east_scale) * dem->east_scale;
807 vik_dem_east_north_to_xy ( dem, start_eas, start_nor, &start_x, &start_y );
809 /* TODO: why start_x and start_y are -1 -- rounding error from above? */
811 counter.zone = dem->utm_zone;
812 counter.letter = dem->utm_letter;
814 for ( x=start_x, counter.easting = start_eas; counter.easting <= end_eas; counter.easting += dem->east_scale * skip_factor, x += skip_factor ) {
815 if ( x > 0 && x < dem->n_columns ) {
816 column = g_ptr_array_index ( dem->columns, x );
817 for ( y=start_y, counter.northing = start_nor; counter.northing <= end_nor; counter.northing += dem->north_scale * skip_factor, y += skip_factor ) {
818 if ( y > column->n_points )
820 elev = column->points[y];
821 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev )
823 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
828 vik_coord_load_from_utm(&tmp, vik_viewport_get_coord_mode(vp), &counter);
829 vik_viewport_coord_to_screen(vp, &tmp, &a, &b);
830 if ( elev == VIK_DEM_INVALID_ELEVATION )
831 ; /* don't draw it */
832 else if ( elev <= 0 )
833 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, a-1, b-1, 2, 2 );
835 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 );
843 /* return the continent for the specified lat, lon */
845 static const gchar *srtm_continent_dir ( gint lat, gint lon )
847 extern const char *_srtm_continent_data[];
848 static GHashTable *srtm_continent = NULL;
849 const gchar *continent;
852 if (!srtm_continent) {
855 srtm_continent = g_hash_table_new(g_str_hash, g_str_equal);
856 s = _srtm_continent_data;
857 while (*s != (gchar *)-1) {
860 g_hash_table_insert(srtm_continent, (gpointer) *s, (gpointer) continent);
866 g_snprintf(name, sizeof(name), "%c%02d%c%03d",
867 (lat >= 0) ? 'N' : 'S', ABS(lat),
868 (lon >= 0) ? 'E' : 'W', ABS(lon));
870 return(g_hash_table_lookup(srtm_continent, name));
873 static void dem_layer_draw ( VikDEMLayer *vdl, VikViewport *vp )
875 GList *dems_iter = vdl->files;
879 /* search for SRTM3 90m */
881 if ( vdl->source == DEM_SOURCE_SRTM )
882 srtm_draw_existence ( vp );
883 #ifdef VIK_CONFIG_DEM24K
884 else if ( vdl->source == DEM_SOURCE_DEM24K )
885 dem24k_draw_existence ( vp );
888 while ( dems_iter ) {
889 dem = a_dems_get ( (const char *) (dems_iter->data) );
891 vik_dem_layer_draw_dem ( vdl, vp, dem );
892 dems_iter = dems_iter->next;
896 static void dem_layer_free ( VikDEMLayer *vdl )
900 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
901 g_object_unref ( vdl->gcs[i] );
904 if ( vdl->gcsgradient )
905 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
906 g_object_unref ( vdl->gcsgradient[i] );
907 g_free ( vdl->gcsgradient );
909 a_dems_list_free ( vdl->files );
912 VikDEMLayer *dem_layer_create ( VikViewport *vp )
914 VikDEMLayer *vdl = dem_layer_new ( vp );
917 /* TODO: share GCS between layers */
918 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ ) {
920 vdl->gcs[i] = vik_viewport_new_gc ( vp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
922 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
923 vdl->gcsgradient[i] = vik_viewport_new_gc ( vp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
927 /**************************************************************
928 **** SOURCES & DOWNLOADING
929 **************************************************************/
935 VikDEMLayer *vdl; /* NULL if not alive */
941 /**************************************************
943 **************************************************/
945 static void srtm_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
948 const gchar *continent_dir;
950 intlat = (int)floor(p->lat);
951 intlon = (int)floor(p->lon);
952 continent_dir = srtm_continent_dir(intlat, intlon);
954 if (!continent_dir) {
956 gchar *msg = g_strdup_printf ( _("No SRTM data available for %f, %f"), p->lat, p->lon );
957 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(p->vdl), msg, VIK_STATUSBAR_INFO );
963 gchar *src_url = g_strdup_printf("%s/%s/%c%02d%c%03d.hgt.zip",
966 (intlat >= 0) ? 'N' : 'S',
968 (intlon >= 0) ? 'E' : 'W',
971 static DownloadFileOptions options = { FALSE, FALSE, NULL, 5, a_check_map_file, NULL, NULL };
972 DownloadResult_t result = a_http_download_get_url ( src_url, NULL, p->dest, &options, NULL );
974 case DOWNLOAD_PARAMETERS_ERROR:
975 case DOWNLOAD_CONTENT_ERROR:
976 case DOWNLOAD_HTTP_ERROR: {
977 gchar *msg = g_strdup_printf ( _("DEM download failure for %f, %f"), p->lat, p->lon );
978 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(p->vdl), msg, VIK_STATUSBAR_INFO );
982 case DOWNLOAD_FILE_WRITE_ERROR: {
983 gchar *msg = g_strdup_printf ( _("DEM write failure for %s"), p->dest );
984 vik_window_statusbar_update ( (VikWindow*)VIK_GTK_WINDOW_FROM_LAYER(p->vdl), msg, VIK_STATUSBAR_INFO );
988 case DOWNLOAD_SUCCESS:
989 case DOWNLOAD_NOT_REQUIRED:
996 static gchar *srtm_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
999 const gchar *continent_dir;
1001 intlat = (int)floor(lat);
1002 intlon = (int)floor(lon);
1003 continent_dir = srtm_continent_dir(intlat, intlon);
1006 continent_dir = "nowhere";
1008 return g_strdup_printf("srtm3-%s%s%c%02d%c%03d.hgt.zip",
1011 (intlat >= 0) ? 'N' : 'S',
1013 (intlon >= 0) ? 'E' : 'W',
1018 /* TODO: generalize */
1019 static void srtm_draw_existence ( VikViewport *vp )
1021 gdouble max_lat, max_lon, min_lat, min_lon;
1022 gchar buf[strlen(MAPS_CACHE_DIR)+strlen(SRTM_CACHE_TEMPLATE)+30];
1025 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
1027 for (i = floor(min_lat); i <= floor(max_lat); i++) {
1028 for (j = floor(min_lon); j <= floor(max_lon); j++) {
1029 const gchar *continent_dir;
1030 if ((continent_dir = srtm_continent_dir(i, j)) == NULL)
1032 g_snprintf(buf, sizeof(buf), SRTM_CACHE_TEMPLATE,
1036 (i >= 0) ? 'N' : 'S',
1038 (j >= 0) ? 'E' : 'W',
1040 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1042 gint x1, y1, x2, y2;
1045 sw.mode = VIK_COORD_LATLON;
1046 ne.north_south = i+1;
1048 ne.mode = VIK_COORD_LATLON;
1049 vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
1050 vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
1051 if ( x1 < 0 ) x1 = 0;
1052 if ( y2 < 0 ) y2 = 0;
1053 vik_viewport_draw_rectangle ( vp, gtk_widget_get_style(GTK_WIDGET(vp))->black_gc,
1054 FALSE, x1, y2, x2-x1, y1-y2 );
1061 /**************************************************
1062 * SOURCE: USGS 24K *
1063 **************************************************/
1065 #ifdef VIK_CONFIG_DEM24K
1067 static void dem24k_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
1069 /* TODO: dest dir */
1070 gchar *cmdline = g_strdup_printf("%s %.03f %.03f",
1071 DEM24K_DOWNLOAD_SCRIPT,
1074 /* FIX: don't use system, use execv or something. check for existence */
1079 static gchar *dem24k_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
1081 return g_strdup_printf("dem24k/%d/%d/%.03f,%.03f.dem",
1088 /* TODO: generalize */
1089 static void dem24k_draw_existence ( VikViewport *vp )
1091 gdouble max_lat, max_lon, min_lat, min_lon;
1092 gchar buf[strlen(MAPS_CACHE_DIR)+40];
1095 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
1097 for (i = floor(min_lat*8)/8; i <= floor(max_lat*8)/8; i+=0.125) {
1098 /* check lat dir first -- faster */
1099 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/",
1102 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1104 for (j = floor(min_lon*8)/8; j <= floor(max_lon*8)/8; j+=0.125) {
1105 /* check lon dir first -- faster */
1106 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/",
1110 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1112 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/%.03f,%.03f.dem",
1118 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1120 gint x1, y1, x2, y2;
1122 sw.east_west = j-0.125;
1123 sw.mode = VIK_COORD_LATLON;
1124 ne.north_south = i+0.125;
1126 ne.mode = VIK_COORD_LATLON;
1127 vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
1128 vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
1129 if ( x1 < 0 ) x1 = 0;
1130 if ( y2 < 0 ) y2 = 0;
1131 vik_viewport_draw_rectangle ( vp, gtk_widget_get_style(GTK_WIDGET(vp))->black_gc,
1132 FALSE, x1, y2, x2-x1, y1-y2 );
1139 /**************************************************
1140 * SOURCES -- DOWNLOADING & IMPORTING TOOL *
1141 **************************************************
1144 static void weak_ref_cb ( gpointer ptr, GObject * dead_vdl )
1146 DEMDownloadParams *p = ptr;
1147 g_mutex_lock ( p->mutex );
1149 g_mutex_unlock ( p->mutex );
1152 /* Try to add file full_path.
1153 * filename will be copied.
1154 * returns FALSE if file does not exists, TRUE otherwise.
1156 static gboolean dem_layer_add_file ( VikDEMLayer *vdl, const gchar *filename )
1158 if ( g_file_test(filename, G_FILE_TEST_EXISTS) == TRUE ) {
1159 /* only load if file size is not 0 (not in progress) */
1161 (void)g_stat ( filename, &sb );
1163 gchar *duped_path = g_strdup(filename);
1164 vdl->files = g_list_prepend ( vdl->files, duped_path );
1165 a_dems_load ( duped_path );
1166 g_debug("%s: %s", __FUNCTION__, duped_path);
1173 static void dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
1175 if ( p->source == DEM_SOURCE_SRTM )
1176 srtm_dem_download_thread ( p, threaddata );
1177 #ifdef VIK_CONFIG_DEM24K
1178 else if ( p->source == DEM_SOURCE_DEM24K )
1179 dem24k_dem_download_thread ( p, threaddata );
1184 //gdk_threads_enter();
1185 g_mutex_lock ( p->mutex );
1187 g_object_weak_unref ( G_OBJECT(p->vdl), weak_ref_cb, p );
1189 if ( dem_layer_add_file ( p->vdl, p->dest ) )
1190 vik_layer_emit_update ( VIK_LAYER(p->vdl) ); // NB update from background thread
1192 g_mutex_unlock ( p->mutex );
1193 //gdk_threads_leave();
1197 static void free_dem_download_params ( DEMDownloadParams *p )
1199 vik_mutex_free ( p->mutex );
1204 static gpointer dem_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1210 * Display a simple dialog with information about the DEM file at this location
1212 static void dem_layer_file_info ( GtkWidget *widget, struct LatLon *ll )
1214 gint intlat, intlon;
1215 const gchar *continent_dir;
1217 intlat = (int)floor(ll->lat);
1218 intlon = (int)floor(ll->lon);
1219 continent_dir = srtm_continent_dir(intlat, intlon);
1221 gchar *source = NULL;
1222 if ( continent_dir )
1223 source = g_strdup_printf ( "%s/%s/%c%02d%c%03d.hgt.zip",
1226 (intlat >= 0) ? 'N' : 'S',
1228 (intlon >= 0) ? 'E' : 'W',
1231 // Probably not over any land...
1232 source = g_strdup ( _("No DEM File Available") );
1234 gchar *filename = NULL;
1235 gchar *dem_file = NULL;
1236 #ifdef VIK_CONFIG_DEM24K
1237 dem_file = dem24k_lat_lon_to_dest_fn ( ll->lat, ll->lon );
1239 dem_file = srtm_lat_lon_to_dest_fn ( ll->lat, ll->lon );
1241 gchar *message = NULL;
1243 filename = g_strdup_printf ( "%s%s", MAPS_CACHE_DIR, dem_file );
1245 if ( g_file_test ( filename, G_FILE_TEST_EXISTS ) ) {
1246 // Get some timestamp information of the file
1248 if ( g_stat ( filename, &stat_buf ) == 0 ) {
1250 strftime ( time_buf, sizeof(time_buf), "%c", gmtime((const time_t *)&stat_buf.st_mtime) );
1251 message = g_strdup_printf ( _("\nSource: %s\n\nDEM File: %s\nDEM File Timestamp: %s"), source, filename, time_buf );
1255 message = g_strdup_printf ( _("Source: %s\n\nNo DEM File!"), source );
1258 a_dialog_info_msg ( GTK_WINDOW(gtk_widget_get_toplevel(widget)), message );
1262 g_free ( dem_file );
1263 g_free ( filename );
1266 static gboolean dem_layer_download_release ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1269 static struct LatLon ll;
1272 gchar *dem_file = NULL;
1274 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1275 vik_coord_to_latlon ( &coord, &ll );
1278 if ( vdl->source == DEM_SOURCE_SRTM )
1279 dem_file = srtm_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1280 #ifdef VIK_CONFIG_DEM24K
1281 else if ( vdl->source == DEM_SOURCE_DEM24K )
1282 dem_file = dem24k_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1288 full_path = g_strdup_printf("%s%s", MAPS_CACHE_DIR, dem_file );
1290 g_debug("%s: %s", __FUNCTION__, full_path);
1292 if ( event->button == 1 ) {
1293 // TODO: check if already in filelist
1294 if ( ! dem_layer_add_file(vdl, full_path) ) {
1295 gchar *tmp = g_strdup_printf ( _("Downloading DEM %s"), dem_file );
1296 DEMDownloadParams *p = g_malloc(sizeof(DEMDownloadParams));
1297 p->dest = g_strdup(full_path);
1301 p->mutex = vik_mutex_new();
1302 p->source = vdl->source;
1303 g_object_weak_ref(G_OBJECT(p->vdl), weak_ref_cb, p );
1305 a_background_thread ( BACKGROUND_POOL_REMOTE,
1306 VIK_GTK_WINDOW_FROM_LAYER(vdl), tmp,
1307 (vik_thr_func) dem_download_thread, p,
1308 (vik_thr_free_func) free_dem_download_params, NULL, 1 );
1313 vik_layer_emit_update ( VIK_LAYER(vdl) );
1316 if ( !vdl->right_click_menu ) {
1318 vdl->right_click_menu = GTK_MENU ( gtk_menu_new () );
1320 item = gtk_image_menu_item_new_with_mnemonic ( _("_Show DEM File Information") );
1321 gtk_image_menu_item_set_image ( (GtkImageMenuItem*)item, gtk_image_new_from_stock (GTK_STOCK_INFO, GTK_ICON_SIZE_MENU) );
1322 g_signal_connect ( G_OBJECT(item), "activate", G_CALLBACK(dem_layer_file_info), &ll );
1323 gtk_menu_shell_append (GTK_MENU_SHELL(vdl->right_click_menu), item);
1326 gtk_menu_popup ( vdl->right_click_menu, NULL, NULL, NULL, NULL, event->button, event->time );
1327 gtk_widget_show_all ( GTK_WIDGET(vdl->right_click_menu) );
1330 g_free ( dem_file );
1331 g_free ( full_path );
1336 static gboolean dem_layer_download_click ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1338 /* choose & keep track of cache dir
1339 * download in background thread
1340 * download over area */