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/gi18n.h>
40 #include "background.h"
42 #include "vikmapslayer.h"
43 #include "vikdemlayer.h"
46 #include "icons/icons.h"
48 #define MAPS_CACHE_DIR maps_layer_default_dir()
50 #define SRTM_CACHE_TEMPLATE "%ssrtm3-%s%s%c%02d%c%03d.hgt.zip"
51 #define SRTM_HTTP_SITE "dds.cr.usgs.gov"
52 #define SRTM_HTTP_URI "/srtm/version2_1/SRTM3/"
54 #ifdef VIK_CONFIG_DEM24K
55 #define DEM24K_DOWNLOAD_SCRIPT "dem24k.pl"
58 #define UNUSED_LINE_THICKNESS 3
60 static VikDEMLayer *dem_layer_new ( VikViewport *vvp );
61 static void dem_layer_draw ( VikDEMLayer *vdl, VikViewport *vp );
62 static void dem_layer_free ( VikDEMLayer *vdl );
63 static VikDEMLayer *dem_layer_create ( VikViewport *vp );
64 static const gchar* dem_layer_tooltip( VikDEMLayer *vdl );
65 static void dem_layer_marshall( VikDEMLayer *vdl, guint8 **data, gint *len );
66 static VikDEMLayer *dem_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
67 static gboolean dem_layer_set_param ( VikDEMLayer *vdl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
68 static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gboolean is_file_operation );
69 static void dem_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file );
70 static void srtm_draw_existence ( VikViewport *vp );
72 #ifdef VIK_CONFIG_DEM24K
73 static void dem24k_draw_existence ( VikViewport *vp );
76 /* Upped upper limit incase units are feet */
77 static VikLayerParamScale param_scales[] = {
82 static gchar *params_source[] = {
83 "SRTM Global 90m (3 arcsec)",
84 #ifdef VIK_CONFIG_DEM24K
90 static gchar *params_type[] = {
91 N_("Absolute height"),
92 N_("Height gradient"),
96 enum { DEM_SOURCE_SRTM,
97 #ifdef VIK_CONFIG_DEM24K
102 enum { DEM_TYPE_HEIGHT = 0,
107 static VikLayerParamData color_default ( void ) {
108 VikLayerParamData data; gdk_color_parse ( "blue", &data.c ); return data;
111 static VikLayerParamData source_default ( void ) { return VIK_LPD_UINT ( DEM_SOURCE_SRTM ); }
112 static VikLayerParamData type_default ( void ) { return VIK_LPD_UINT ( DEM_TYPE_HEIGHT ); }
113 static VikLayerParamData min_elev_default ( void ) { return VIK_LPD_DOUBLE ( 0.0 ); }
114 static VikLayerParamData max_elev_default ( void ) { return VIK_LPD_DOUBLE ( 1000.0 ); }
116 static VikLayerParam dem_layer_params[] = {
117 { VIK_LAYER_DEM, "files", VIK_LAYER_PARAM_STRING_LIST, VIK_LAYER_GROUP_NONE, N_("DEM Files:"), VIK_LAYER_WIDGET_FILELIST, NULL, NULL, NULL, NULL },
118 { VIK_LAYER_DEM, "source", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Download Source:"), VIK_LAYER_WIDGET_RADIOGROUP_STATIC, params_source, NULL, NULL, source_default },
119 { VIK_LAYER_DEM, "color", VIK_LAYER_PARAM_COLOR, VIK_LAYER_GROUP_NONE, N_("Min Elev Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, color_default },
120 { VIK_LAYER_DEM, "type", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Type:"), VIK_LAYER_WIDGET_RADIOGROUP_STATIC, params_type, NULL, NULL, type_default },
121 { VIK_LAYER_DEM, "min_elev", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_GROUP_NONE, N_("Min Elev:"), VIK_LAYER_WIDGET_SPINBUTTON, param_scales + 0, NULL, NULL, min_elev_default },
122 { VIK_LAYER_DEM, "max_elev", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_GROUP_NONE, N_("Max Elev:"), VIK_LAYER_WIDGET_SPINBUTTON, param_scales + 0, NULL, NULL, max_elev_default },
126 enum { PARAM_FILES=0, PARAM_SOURCE, PARAM_COLOR, PARAM_TYPE, PARAM_MIN_ELEV, PARAM_MAX_ELEV, NUM_PARAMS };
128 static gpointer dem_layer_download_create ( VikWindow *vw, VikViewport *vvp);
129 static gboolean dem_layer_download_release ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp );
130 static gboolean dem_layer_download_click ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp );
132 static VikToolInterface dem_tools[] = {
133 { { "DEMDownload", "vik-icon-DEM Download", N_("_DEM Download"), NULL, N_("DEM Download"), 0 },
134 (VikToolConstructorFunc) dem_layer_download_create, NULL, NULL, NULL,
135 (VikToolMouseFunc) dem_layer_download_click, NULL, (VikToolMouseFunc) dem_layer_download_release,
136 (VikToolKeyFunc) NULL,
138 GDK_CURSOR_IS_PIXMAP, &cursor_demdl_pixbuf },
143 The first entry is blue for a default 'sea' colour,
144 however the value used by the corresponding gc can be configured as part of the DEM layer properties.
145 The other colours, shaded from brown to white are used to give an indication of height.
147 static gchar *dem_height_colors[] = {
149 "#9b793c", "#9c7d40", "#9d8144", "#9e8549", "#9f894d", "#a08d51", "#a29156", "#a3955a", "#a4995e", "#a69d63",
150 "#a89f65", "#aaa267", "#ada569", "#afa76b", "#b1aa6d", "#b4ad6f", "#b6b071", "#b9b373", "#bcb676", "#beb978",
151 "#c0bc7a", "#c2c07d", "#c4c37f", "#c6c681", "#c8ca84", "#cacd86", "#ccd188", "#cfd58b", "#c2ce84", "#b5c87e",
152 "#a9c278", "#9cbb71", "#8fb56b", "#83af65", "#76a95e", "#6aa358", "#5e9d52", "#63a055", "#69a458", "#6fa85c",
153 "#74ac5f", "#7ab063", "#80b467", "#86b86a", "#8cbc6e", "#92c072", "#94c175", "#97c278", "#9ac47c", "#9cc57f",
154 "#9fc682", "#a2c886", "#a4c989", "#a7cb8d", "#aacd91", "#afce99", "#b5d0a1", "#bbd2aa", "#c0d3b2", "#c6d5ba",
155 "#ccd7c3", "#d1d9cb", "#d7dbd4", "#DDDDDD", "#e0e0e0", "#e4e4e4", "#e8e8e8", "#ebebeb", "#efefef", "#f3f3f3",
156 "#f7f7f7", "#fbfbfb", "#ffffff"
159 static const guint DEM_N_HEIGHT_COLORS = sizeof(dem_height_colors)/sizeof(dem_height_colors[0]);
162 "#9b793c", "#9e8549", "#a29156", "#a69d63", "#ada569", "#b4ad6f", "#bcb676", "#c2c07d", "#c8ca84", "#cfd58b",
163 "#a9c278", "#83af65", "#5e9d52", "#6fa85c", "#80b467", "#92c072", "#9ac47c", "#a2c886", "#aacd91", "#bbd2aa",
164 "#ccd7c3", "#DDDDDD", "#e8e8e8", "#f3f3f3", "#FFFFFF"
168 static gchar *dem_gradient_colors[] = {
170 "#000000", "#000011", "#000022", "#000033", "#000044", "#00004c", "#000055", "#00005d", "#000066", "#00006e",
171 "#000077", "#00007f", "#000088", "#000090", "#000099", "#0000a1", "#0000aa", "#0000b2", "#0000bb", "#0000c3",
172 "#0000cc", "#0000d4", "#0000dd", "#0000e5", "#0000ee", "#0000f6", "#0000ff", "#0008f7", "#0011ee", "#0019e6",
173 "#0022dd", "#002ad5", "#0033cc", "#003bc4", "#0044bb", "#004cb3", "#0055aa", "#005da2", "#006699", "#006e91",
174 "#007788", "#007f80", "#008877", "#00906f", "#009966", "#00a15e", "#00aa55", "#00b24d", "#00bb44", "#00c33c",
175 "#00cc33", "#00d42b", "#00dd22", "#00e51a", "#00ee11", "#00f609", "#00ff00", "#08f700", "#11ee00", "#19e600",
176 "#22dd00", "#2ad500", "#33cc00", "#3bc400", "#44bb00", "#4cb300", "#55aa00", "#5da200", "#669900", "#6e9100",
177 "#778800", "#7f8000", "#887700", "#906f00", "#996600", "#a15e00", "#aa5500", "#b24d00", "#bb4400", "#c33c00",
178 "#cc3300", "#d42b00", "#dd2200", "#e51a00", "#ee1100", "#f60900", "#ff0000",
182 static const guint DEM_N_GRADIENT_COLORS = sizeof(dem_gradient_colors)/sizeof(dem_gradient_colors[0]);
185 VikLayerInterface vik_dem_layer_interface = {
192 sizeof(dem_tools) / sizeof(dem_tools[0]),
201 (VikLayerFuncCreate) dem_layer_create,
202 (VikLayerFuncRealize) NULL,
203 (VikLayerFuncPostRead) dem_layer_post_read,
204 (VikLayerFuncFree) dem_layer_free,
206 (VikLayerFuncProperties) NULL,
207 (VikLayerFuncDraw) dem_layer_draw,
208 (VikLayerFuncChangeCoordMode) NULL,
210 (VikLayerFuncSetMenuItemsSelection) NULL,
211 (VikLayerFuncGetMenuItemsSelection) NULL,
213 (VikLayerFuncAddMenuItems) NULL,
214 (VikLayerFuncSublayerAddMenuItems) NULL,
216 (VikLayerFuncSublayerRenameRequest) NULL,
217 (VikLayerFuncSublayerToggleVisible) NULL,
218 (VikLayerFuncSublayerTooltip) NULL,
219 (VikLayerFuncLayerTooltip) dem_layer_tooltip,
220 (VikLayerFuncLayerSelected) NULL,
222 (VikLayerFuncMarshall) dem_layer_marshall,
223 (VikLayerFuncUnmarshall) dem_layer_unmarshall,
225 (VikLayerFuncSetParam) dem_layer_set_param,
226 (VikLayerFuncGetParam) dem_layer_get_param,
228 (VikLayerFuncReadFileData) NULL,
229 (VikLayerFuncWriteFileData) NULL,
231 (VikLayerFuncDeleteItem) NULL,
232 (VikLayerFuncCutItem) NULL,
233 (VikLayerFuncCopyItem) NULL,
234 (VikLayerFuncPasteItem) NULL,
235 (VikLayerFuncFreeCopiedItem) NULL,
236 (VikLayerFuncDragDropRequest) NULL,
238 (VikLayerFuncSelectClick) NULL,
239 (VikLayerFuncSelectMove) NULL,
240 (VikLayerFuncSelectRelease) NULL,
241 (VikLayerFuncSelectedViewportMenu) NULL,
244 struct _VikDEMLayer {
256 GType vik_dem_layer_get_type ()
258 static GType vdl_type = 0;
262 static const GTypeInfo vdl_info =
264 sizeof (VikDEMLayerClass),
265 NULL, /* base_init */
266 NULL, /* base_finalize */
267 NULL, /* class init */
268 NULL, /* class_finalize */
269 NULL, /* class_data */
270 sizeof (VikDEMLayer),
272 NULL /* instance init */
274 vdl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikDEMLayer", &vdl_info, 0 );
280 static const gchar* dem_layer_tooltip( VikDEMLayer *vdl )
282 static gchar tmp_buf[100];
283 g_snprintf (tmp_buf, sizeof(tmp_buf), _("Number of files: %d"), g_list_length (vdl->files));
287 static void dem_layer_marshall( VikDEMLayer *vdl, guint8 **data, gint *len )
289 vik_layer_marshall_params ( VIK_LAYER(vdl), data, len );
292 static VikDEMLayer *dem_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
294 VikDEMLayer *rv = dem_layer_new ( vvp );
297 /* TODO: share GCS between layers */
298 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ ) {
300 rv->gcs[i] = vik_viewport_new_gc_from_color ( vvp, &(rv->color), UNUSED_LINE_THICKNESS );
302 rv->gcs[i] = vik_viewport_new_gc ( vvp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
304 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
305 rv->gcsgradient[i] = vik_viewport_new_gc ( vvp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
307 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
311 /* Structure for DEM data used in background thread */
314 } dem_load_thread_data;
317 * Function for starting the DEM file loading as a background thread
319 static int dem_layer_load_list_thread ( dem_load_thread_data *dltd, gpointer threaddata )
321 int result = 0; // Default to good
323 if ( a_dems_load_list ( &(dltd->vdl->files), threaddata ) ) {
328 // ATM as each file is processed the screen is not updated (no mechanism exposed to a_dems_load_list)
329 // Thus force draw only at the end, as loading is complete/aborted
330 //gdk_threads_enter();
331 // Test is helpful to prevent Gtk-CRITICAL warnings if the program is exitted whilst loading
332 if ( IS_VIK_LAYER(dltd->vdl) )
333 vik_layer_emit_update ( VIK_LAYER(dltd->vdl) ); // NB update from background thread
334 //gdk_threads_leave();
339 static void dem_layer_thread_data_free ( dem_load_thread_data *data )
345 static void dem_layer_thread_cancel ( dem_load_thread_data *data )
348 // Instead of freeing the list, leave it as partially processed
349 // Thus we can see/use what was done
353 * Process the list of DEM files and convert each one to a relative path
355 static GList *dem_layer_convert_to_relative_filenaming ( GList *files )
357 gchar *cwd = g_get_current_dir();
361 GList *relfiles = NULL;
364 gchar *file = g_strdup ( file_GetRelativeFilename ( cwd, files->data ) );
365 relfiles = g_list_prepend ( relfiles, file );
372 // Replacing current list, so delete old values first.
375 g_free ( iter->data );
378 g_list_free ( files );
386 gboolean dem_layer_set_param ( VikDEMLayer *vdl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
390 case PARAM_COLOR: vdl->color = data.c; gdk_gc_set_rgb_fg_color ( vdl->gcs[0], &(vdl->color) ); break;
391 case PARAM_SOURCE: vdl->source = data.u; break;
392 case PARAM_TYPE: vdl->type = data.u; break;
394 /* Convert to store internally
395 NB file operation always in internal units (metres) */
396 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
397 vdl->min_elev = VIK_FEET_TO_METERS(data.d);
399 vdl->min_elev = data.d;
402 /* Convert to store internally
403 NB file operation always in internal units (metres) */
404 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
405 vdl->max_elev = VIK_FEET_TO_METERS(data.d);
407 vdl->max_elev = data.d;
411 // Clear out old settings - if any commonalities with new settings they will have to be read again
412 a_dems_list_free ( vdl->files );
413 // Set file list so any other intermediate screen drawing updates will show currently loaded DEMs by the working thread
414 vdl->files = data.sl;
415 // No need for thread if no files
418 dem_load_thread_data *dltd = g_malloc ( sizeof(dem_load_thread_data) );
420 dltd->vdl->files = data.sl;
422 a_background_thread ( VIK_GTK_WINDOW_FROM_WIDGET(vp),
424 (vik_thr_func) dem_layer_load_list_thread,
426 (vik_thr_free_func) dem_layer_thread_data_free,
427 (vik_thr_free_func) dem_layer_thread_cancel,
428 g_list_length ( data.sl ) ); // Number of DEM files
436 static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gboolean is_file_operation )
438 VikLayerParamData rv;
443 if ( is_file_operation )
444 // Save in relative format if necessary
445 if ( a_vik_get_file_ref_format() == VIK_FILE_REF_FORMAT_RELATIVE )
446 rv.sl = dem_layer_convert_to_relative_filenaming ( rv.sl );
448 case PARAM_SOURCE: rv.u = vdl->source; break;
449 case PARAM_TYPE: rv.u = vdl->type; break;
450 case PARAM_COLOR: rv.c = vdl->color; break;
452 /* Convert for display in desired units
453 NB file operation always in internal units (metres) */
454 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
455 rv.d = VIK_METERS_TO_FEET(vdl->min_elev);
457 rv.d = vdl->min_elev;
460 /* Convert for display in desired units
461 NB file operation always in internal units (metres) */
462 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
463 rv.d = VIK_METERS_TO_FEET(vdl->max_elev);
465 rv.d = vdl->max_elev;
471 static void dem_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file )
473 /* nothing ATM, but keep in case it's needed the future */
476 static VikDEMLayer *dem_layer_new ( VikViewport *vvp )
478 VikDEMLayer *vdl = VIK_DEM_LAYER ( g_object_new ( VIK_DEM_LAYER_TYPE, NULL ) );
480 vik_layer_set_type ( VIK_LAYER(vdl), VIK_LAYER_DEM );
484 vdl->gcs = g_malloc(sizeof(GdkGC *)*DEM_N_HEIGHT_COLORS);
485 vdl->gcsgradient = g_malloc(sizeof(GdkGC *)*DEM_N_GRADIENT_COLORS);
486 /* make new gcs only if we need it (copy layer -> use old) */
488 // Ensure the base GC is available so the default colour can be applied
489 if ( vvp ) vdl->gcs[0] = vik_viewport_new_gc ( vvp, "#0000FF", 1 );
491 vik_layer_set_defaults ( VIK_LAYER(vdl), vvp );
497 static inline guint16 get_height_difference(gint16 elev, gint16 new_elev)
499 if(new_elev == VIK_DEM_INVALID_ELEVATION)
502 return abs(new_elev - elev);
506 static void vik_dem_layer_draw_dem ( VikDEMLayer *vdl, VikViewport *vp, VikDEM *dem )
508 VikDEMColumn *column, *prevcolumn, *nextcolumn;
510 struct LatLon dem_northeast, dem_southwest;
511 gdouble max_lat, max_lon, min_lat, min_lon;
513 /**** Check if viewport and DEM data overlap ****/
515 /* get min, max lat/lon of viewport */
516 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
518 /* get min, max lat/lon of DEM data */
519 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
520 dem_northeast.lat = dem->max_north / 3600.0;
521 dem_northeast.lon = dem->max_east / 3600.0;
522 dem_southwest.lat = dem->min_north / 3600.0;
523 dem_southwest.lon = dem->min_east / 3600.0;
524 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
525 struct UTM dem_northeast_utm, dem_southwest_utm;
526 dem_northeast_utm.northing = dem->max_north;
527 dem_northeast_utm.easting = dem->max_east;
528 dem_southwest_utm.northing = dem->min_north;
529 dem_southwest_utm.easting = dem->min_east;
530 dem_northeast_utm.zone = dem_southwest_utm.zone = dem->utm_zone;
531 dem_northeast_utm.letter = dem_southwest_utm.letter = dem->utm_letter;
533 a_coords_utm_to_latlon(&dem_northeast_utm, &dem_northeast);
534 a_coords_utm_to_latlon(&dem_southwest_utm, &dem_southwest);
537 if ( (max_lat > dem_northeast.lat && min_lat > dem_northeast.lat) ||
538 (max_lat < dem_southwest.lat && min_lat < dem_southwest.lat) )
540 else if ( (max_lon > dem_northeast.lon && min_lon > dem_northeast.lon) ||
541 (max_lon < dem_southwest.lon && min_lon < dem_southwest.lon) )
543 /* else they overlap */
545 /**** End Overlap Check ****/
546 /* boxes to show where we have DEM instead of actually drawing the DEM.
547 * useful if we want to see what areas we have coverage for (if we want
548 * to get elevation data for a track) but don't want to cover the map.
552 /* draw a box if a DEM is loaded. in future I'd like to add an option for this
553 * this is useful if we want to see what areas we have dem for but don't want to
554 * cover the map (or maybe we just need translucent DEM?) */
556 VikCoord demne, demsw;
558 vik_coord_load_from_latlon(&demne, vik_viewport_get_coord_mode(vp), &dem_northeast);
559 vik_coord_load_from_latlon(&demsw, vik_viewport_get_coord_mode(vp), &dem_southwest);
561 vik_viewport_coord_to_screen ( vp, &demne, &x1, &y1 );
562 vik_viewport_coord_to_screen ( vp, &demsw, &x2, &y2 );
564 if ( x1 > vik_viewport_get_width(vp) ) x1=vik_viewport_get_width(vp);
565 if ( y2 > vik_viewport_get_height(vp) ) y2=vik_viewport_get_height(vp);
566 if ( x2 < 0 ) x2 = 0;
567 if ( y1 < 0 ) y1 = 0;
568 vik_viewport_draw_rectangle ( vp, gtk_widget_get_style(GTK_WIDGET(vp))->black_gc,
569 FALSE, x2, y1, x1-x2, y2-y1 );
574 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
575 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
577 gdouble max_lat_as, max_lon_as, min_lat_as, min_lon_as;
578 gdouble start_lat_as, end_lat_as, start_lon_as, end_lon_as;
580 gdouble start_lat, end_lat, start_lon, end_lon;
582 struct LatLon counter;
584 guint x, y, start_x, start_y;
588 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 80 ); /* todo: smarter calculation. */
590 gdouble nscale_deg = dem->north_scale / ((gdouble) 3600);
591 gdouble escale_deg = dem->east_scale / ((gdouble) 3600);
593 max_lat_as = max_lat * 3600;
594 min_lat_as = min_lat * 3600;
595 max_lon_as = max_lon * 3600;
596 min_lon_as = min_lon * 3600;
598 start_lat_as = MAX(min_lat_as, dem->min_north);
599 end_lat_as = MIN(max_lat_as, dem->max_north);
600 start_lon_as = MAX(min_lon_as, dem->min_east);
601 end_lon_as = MIN(max_lon_as, dem->max_east);
603 start_lat = floor(start_lat_as / dem->north_scale) * nscale_deg;
604 end_lat = ceil (end_lat_as / dem->north_scale) * nscale_deg;
605 start_lon = floor(start_lon_as / dem->east_scale) * escale_deg;
606 end_lon = ceil (end_lon_as / dem->east_scale) * escale_deg;
608 vik_dem_east_north_to_xy ( dem, start_lon_as, start_lat_as, &start_x, &start_y );
609 guint gradient_skip_factor = 1;
610 if(vdl->type == DEM_TYPE_GRADIENT)
611 gradient_skip_factor = skip_factor;
613 /* verify sane elev interval */
614 if ( vdl->max_elev <= vdl->min_elev )
615 vdl->max_elev = vdl->min_elev + 1;
617 for ( x=start_x, counter.lon = start_lon; counter.lon <= end_lon+escale_deg*skip_factor; counter.lon += escale_deg * skip_factor, x += skip_factor ) {
618 // NOTE: ( counter.lon <= end_lon + ESCALE_DEG*SKIP_FACTOR ) is neccessary so in high zoom modes,
619 // the leftmost column does also get drawn, if the center point is out of viewport.
620 if ( x < dem->n_columns ) {
621 column = g_ptr_array_index ( dem->columns, x );
622 // get previous and next column. catch out-of-bound.
624 new_x -= gradient_skip_factor;
626 prevcolumn = g_ptr_array_index ( dem->columns, x+1);
628 prevcolumn = g_ptr_array_index ( dem->columns, new_x);
630 new_x += gradient_skip_factor;
631 if(new_x >= dem->n_columns)
632 nextcolumn = g_ptr_array_index ( dem->columns, x-1);
634 nextcolumn = g_ptr_array_index ( dem->columns, new_x);
636 for ( y=start_y, counter.lat = start_lat; counter.lat <= end_lat; counter.lat += nscale_deg * skip_factor, y += skip_factor ) {
637 if ( y > column->n_points )
640 elev = column->points[y];
642 // calculate bounding box for drawing
643 gint box_x, box_y, box_width, box_height;
646 box_c.lat += (nscale_deg * skip_factor)/2;
647 box_c.lon -= (escale_deg * skip_factor)/2;
648 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
649 vik_viewport_coord_to_screen(vp, &tmp, &box_x, &box_y);
650 // catch box at borders
655 box_c.lat -= nscale_deg * skip_factor;
656 box_c.lon += escale_deg * skip_factor;
657 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
658 vik_viewport_coord_to_screen(vp, &tmp, &box_width, &box_height);
661 // catch box at borders
662 if(box_width < 0 || box_height < 0)
663 continue; // skip this. this is out of our viewport anyway. FIXME: why?
665 gboolean below_minimum = FALSE;
666 if(vdl->type == DEM_TYPE_HEIGHT) {
667 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev ) {
668 // Prevent 'elev - vdl->min_elev' from being negative so can safely use as array index
669 elev = ceil ( vdl->min_elev );
670 below_minimum = TRUE;
672 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
673 elev = vdl->max_elev;
677 if(box_width < 0 || box_height < 0) // FIXME: why does this happen?
680 if(vdl->type == DEM_TYPE_GRADIENT) {
681 if( elev == VIK_DEM_INVALID_ELEVATION ) {
684 // calculate and sum gradient in all directions
688 // calculate gradient from height points all around the current one
689 new_y = y - gradient_skip_factor;
692 change += get_height_difference(elev, prevcolumn->points[new_y]);
693 change += get_height_difference(elev, column->points[new_y]);
694 change += get_height_difference(elev, nextcolumn->points[new_y]);
696 change += get_height_difference(elev, prevcolumn->points[y]);
697 change += get_height_difference(elev, nextcolumn->points[y]);
699 new_y = y + gradient_skip_factor;
700 if(new_y >= column->n_points)
702 change += get_height_difference(elev, prevcolumn->points[new_y]);
703 change += get_height_difference(elev, column->points[new_y]);
704 change += get_height_difference(elev, nextcolumn->points[new_y]);
706 change = change / ((skip_factor > 1) ? log(skip_factor) : 0.55); // FIXME: better calc.
708 if(change < vdl->min_elev)
709 // Prevent 'change - vdl->min_elev' from being negative so can safely use as array index
710 change = ceil ( vdl->min_elev );
712 if(change > vdl->max_elev)
713 change = vdl->max_elev;
715 // void vik_viewport_draw_rectangle ( VikViewport *vvp, GdkGC *gc, gboolean filled, gint x1, gint y1, gint x2, gint y2 );
716 vik_viewport_draw_rectangle(vp, vdl->gcsgradient[(gint)floor(((change - vdl->min_elev)/(vdl->max_elev - vdl->min_elev))*(DEM_N_GRADIENT_COLORS-2))+1], TRUE, box_x, box_y, box_width, box_height);
719 if(vdl->type == DEM_TYPE_HEIGHT) {
720 if ( elev == VIK_DEM_INVALID_ELEVATION )
721 ; /* don't draw it */
722 else if ( elev <= 0 || below_minimum )
723 /* If 'sea' colour or below the defined mininum draw in the configurable colour */
724 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, box_x, box_y, box_width, box_height);
726 vik_viewport_draw_rectangle(vp, vdl->gcs[(gint)floor(((elev - vdl->min_elev)/(vdl->max_elev - vdl->min_elev))*(DEM_N_HEIGHT_COLORS-2))+1], TRUE, box_x, box_y, box_width, box_height);
733 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
734 gdouble max_nor, max_eas, min_nor, min_eas;
735 gdouble start_nor, start_eas, end_nor, end_eas;
739 guint x, y, start_x, start_y;
741 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
744 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 10 ); /* todo: smarter calculation. */
746 VikCoord tleft, tright, bleft, bright;
748 vik_viewport_screen_to_coord ( vp, 0, 0, &tleft );
749 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), 0, &tright );
750 vik_viewport_screen_to_coord ( vp, 0, vik_viewport_get_height(vp), &bleft );
751 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), vik_viewport_get_height(vp), &bright );
754 vik_coord_convert(&tleft, VIK_COORD_UTM);
755 vik_coord_convert(&tright, VIK_COORD_UTM);
756 vik_coord_convert(&bleft, VIK_COORD_UTM);
757 vik_coord_convert(&bright, VIK_COORD_UTM);
759 max_nor = MAX(tleft.north_south, tright.north_south);
760 min_nor = MIN(bleft.north_south, bright.north_south);
761 max_eas = MAX(bright.east_west, tright.east_west);
762 min_eas = MIN(bleft.east_west, tleft.east_west);
764 start_nor = MAX(min_nor, dem->min_north);
765 end_nor = MIN(max_nor, dem->max_north);
766 if ( tleft.utm_zone == dem->utm_zone && bleft.utm_zone == dem->utm_zone
767 && (tleft.utm_letter >= 'N') == (dem->utm_letter >= 'N')
768 && (bleft.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
769 start_eas = MAX(min_eas, dem->min_east);
771 start_eas = dem->min_east;
772 if ( tright.utm_zone == dem->utm_zone && bright.utm_zone == dem->utm_zone
773 && (tright.utm_letter >= 'N') == (dem->utm_letter >= 'N')
774 && (bright.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
775 end_eas = MIN(max_eas, dem->max_east);
777 end_eas = dem->max_east;
779 start_nor = floor(start_nor / dem->north_scale) * dem->north_scale;
780 end_nor = ceil (end_nor / dem->north_scale) * dem->north_scale;
781 start_eas = floor(start_eas / dem->east_scale) * dem->east_scale;
782 end_eas = ceil (end_eas / dem->east_scale) * dem->east_scale;
784 vik_dem_east_north_to_xy ( dem, start_eas, start_nor, &start_x, &start_y );
786 /* TODO: why start_x and start_y are -1 -- rounding error from above? */
788 counter.zone = dem->utm_zone;
789 counter.letter = dem->utm_letter;
791 for ( x=start_x, counter.easting = start_eas; counter.easting <= end_eas; counter.easting += dem->east_scale * skip_factor, x += skip_factor ) {
792 if ( x > 0 && x < dem->n_columns ) {
793 column = g_ptr_array_index ( dem->columns, x );
794 for ( y=start_y, counter.northing = start_nor; counter.northing <= end_nor; counter.northing += dem->north_scale * skip_factor, y += skip_factor ) {
795 if ( y > column->n_points )
797 elev = column->points[y];
798 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev )
800 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
805 vik_coord_load_from_utm(&tmp, vik_viewport_get_coord_mode(vp), &counter);
806 vik_viewport_coord_to_screen(vp, &tmp, &a, &b);
807 if ( elev == VIK_DEM_INVALID_ELEVATION )
808 ; /* don't draw it */
809 else if ( elev <= 0 )
810 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, a-1, b-1, 2, 2 );
812 vik_viewport_draw_rectangle(vp, vdl->gcs[(gint)floor((elev - vdl->min_elev)/(vdl->max_elev - vdl->min_elev)*(DEM_N_HEIGHT_COLORS-2))+1], TRUE, a-1, b-1, 2, 2 );
820 /* return the continent for the specified lat, lon */
822 static const gchar *srtm_continent_dir ( gint lat, gint lon )
824 extern const char *_srtm_continent_data[];
825 static GHashTable *srtm_continent = NULL;
826 const gchar *continent;
829 if (!srtm_continent) {
832 srtm_continent = g_hash_table_new(g_str_hash, g_str_equal);
833 s = _srtm_continent_data;
834 while (*s != (gchar *)-1) {
837 g_hash_table_insert(srtm_continent, (gpointer) *s, (gpointer) continent);
843 g_snprintf(name, sizeof(name), "%c%02d%c%03d",
844 (lat >= 0) ? 'N' : 'S', ABS(lat),
845 (lon >= 0) ? 'E' : 'W', ABS(lon));
847 return(g_hash_table_lookup(srtm_continent, name));
850 static void dem_layer_draw ( VikDEMLayer *vdl, VikViewport *vp )
852 GList *dems_iter = vdl->files;
856 /* search for SRTM3 90m */
858 if ( vdl->source == DEM_SOURCE_SRTM )
859 srtm_draw_existence ( vp );
860 #ifdef VIK_CONFIG_DEM24K
861 else if ( vdl->source == DEM_SOURCE_DEM24K )
862 dem24k_draw_existence ( vp );
865 while ( dems_iter ) {
866 dem = a_dems_get ( (const char *) (dems_iter->data) );
868 vik_dem_layer_draw_dem ( vdl, vp, dem );
869 dems_iter = dems_iter->next;
873 static void dem_layer_free ( VikDEMLayer *vdl )
877 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
878 g_object_unref ( vdl->gcs[i] );
881 if ( vdl->gcsgradient )
882 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
883 g_object_unref ( vdl->gcsgradient[i] );
884 g_free ( vdl->gcsgradient );
886 a_dems_list_free ( vdl->files );
889 VikDEMLayer *dem_layer_create ( VikViewport *vp )
891 VikDEMLayer *vdl = dem_layer_new ( vp );
894 /* TODO: share GCS between layers */
895 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ ) {
897 vdl->gcs[i] = vik_viewport_new_gc ( vp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
899 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
900 vdl->gcsgradient[i] = vik_viewport_new_gc ( vp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
904 /**************************************************************
905 **** SOURCES & DOWNLOADING
906 **************************************************************/
912 VikDEMLayer *vdl; /* NULL if not alive */
918 /**************************************************
920 **************************************************/
922 static void srtm_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
925 const gchar *continent_dir;
927 intlat = (int)floor(p->lat);
928 intlon = (int)floor(p->lon);
929 continent_dir = srtm_continent_dir(intlat, intlon);
931 if (!continent_dir) {
932 g_warning(N_("No SRTM data available for %f, %f"), p->lat, p->lon);
936 gchar *src_fn = g_strdup_printf("%s%s/%c%02d%c%03d.hgt.zip",
939 (intlat >= 0) ? 'N' : 'S',
941 (intlon >= 0) ? 'E' : 'W',
944 static DownloadMapOptions options = { FALSE, FALSE, NULL, 0, a_check_map_file };
945 a_http_download_get_url ( SRTM_HTTP_SITE, src_fn, p->dest, &options, NULL );
949 static gchar *srtm_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
952 const gchar *continent_dir;
954 intlat = (int)floor(lat);
955 intlon = (int)floor(lon);
956 continent_dir = srtm_continent_dir(intlat, intlon);
959 continent_dir = "nowhere";
961 return g_strdup_printf("srtm3-%s%s%c%02d%c%03d.hgt.zip",
964 (intlat >= 0) ? 'N' : 'S',
966 (intlon >= 0) ? 'E' : 'W',
971 /* TODO: generalize */
972 static void srtm_draw_existence ( VikViewport *vp )
974 gdouble max_lat, max_lon, min_lat, min_lon;
975 gchar buf[strlen(MAPS_CACHE_DIR)+strlen(SRTM_CACHE_TEMPLATE)+30];
978 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
980 for (i = floor(min_lat); i <= floor(max_lat); i++) {
981 for (j = floor(min_lon); j <= floor(max_lon); j++) {
982 const gchar *continent_dir;
983 if ((continent_dir = srtm_continent_dir(i, j)) == NULL)
985 g_snprintf(buf, sizeof(buf), SRTM_CACHE_TEMPLATE,
989 (i >= 0) ? 'N' : 'S',
991 (j >= 0) ? 'E' : 'W',
993 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
998 sw.mode = VIK_COORD_LATLON;
999 ne.north_south = i+1;
1001 ne.mode = VIK_COORD_LATLON;
1002 vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
1003 vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
1004 if ( x1 < 0 ) x1 = 0;
1005 if ( y2 < 0 ) y2 = 0;
1006 vik_viewport_draw_rectangle ( vp, gtk_widget_get_style(GTK_WIDGET(vp))->black_gc,
1007 FALSE, x1, y2, x2-x1, y1-y2 );
1014 /**************************************************
1015 * SOURCE: USGS 24K *
1016 **************************************************/
1018 #ifdef VIK_CONFIG_DEM24K
1020 static void dem24k_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
1022 /* TODO: dest dir */
1023 gchar *cmdline = g_strdup_printf("%s %.03f %.03f",
1024 DEM24K_DOWNLOAD_SCRIPT,
1027 /* FIX: don't use system, use execv or something. check for existence */
1032 static gchar *dem24k_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
1034 return g_strdup_printf("dem24k/%d/%d/%.03f,%.03f.dem",
1041 /* TODO: generalize */
1042 static void dem24k_draw_existence ( VikViewport *vp )
1044 gdouble max_lat, max_lon, min_lat, min_lon;
1045 gchar buf[strlen(MAPS_CACHE_DIR)+40];
1048 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
1050 for (i = floor(min_lat*8)/8; i <= floor(max_lat*8)/8; i+=0.125) {
1051 /* check lat dir first -- faster */
1052 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/",
1055 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1057 for (j = floor(min_lon*8)/8; j <= floor(max_lon*8)/8; j+=0.125) {
1058 /* check lon dir first -- faster */
1059 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/",
1063 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1065 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/%.03f,%.03f.dem",
1071 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1073 gint x1, y1, x2, y2;
1075 sw.east_west = j-0.125;
1076 sw.mode = VIK_COORD_LATLON;
1077 ne.north_south = i+0.125;
1079 ne.mode = VIK_COORD_LATLON;
1080 vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
1081 vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
1082 if ( x1 < 0 ) x1 = 0;
1083 if ( y2 < 0 ) y2 = 0;
1084 vik_viewport_draw_rectangle ( vp, gtk_widget_get_style(GTK_WIDGET(vp))->black_gc,
1085 FALSE, x1, y2, x2-x1, y1-y2 );
1092 /**************************************************
1093 * SOURCES -- DOWNLOADING & IMPORTING TOOL *
1094 **************************************************
1097 static void weak_ref_cb ( gpointer ptr, GObject * dead_vdl )
1099 DEMDownloadParams *p = ptr;
1100 g_mutex_lock ( p->mutex );
1102 g_mutex_unlock ( p->mutex );
1105 /* Try to add file full_path.
1106 * filename will be copied.
1107 * returns FALSE if file does not exists, TRUE otherwise.
1109 static gboolean dem_layer_add_file ( VikDEMLayer *vdl, const gchar *filename )
1111 if ( g_file_test(filename, G_FILE_TEST_EXISTS) == TRUE ) {
1112 /* only load if file size is not 0 (not in progress) */
1114 stat ( filename, &sb );
1116 gchar *duped_path = g_strdup(filename);
1117 vdl->files = g_list_prepend ( vdl->files, duped_path );
1118 a_dems_load ( duped_path );
1119 g_debug("%s: %s", __FUNCTION__, duped_path);
1126 static void dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
1128 if ( p->source == DEM_SOURCE_SRTM )
1129 srtm_dem_download_thread ( p, threaddata );
1130 #ifdef VIK_CONFIG_DEM24K
1131 else if ( p->source == DEM_SOURCE_DEM24K )
1132 dem24k_dem_download_thread ( p, threaddata );
1137 //gdk_threads_enter();
1138 g_mutex_lock ( p->mutex );
1140 g_object_weak_unref ( G_OBJECT(p->vdl), weak_ref_cb, p );
1142 if ( dem_layer_add_file ( p->vdl, p->dest ) )
1143 vik_layer_emit_update ( VIK_LAYER(p->vdl) ); // NB update from background thread
1145 g_mutex_unlock ( p->mutex );
1146 //gdk_threads_leave();
1150 static void free_dem_download_params ( DEMDownloadParams *p )
1152 g_mutex_free ( p->mutex );
1157 static gpointer dem_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1163 static gboolean dem_layer_download_release ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1169 gchar *dem_file = NULL;
1171 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1172 vik_coord_to_latlon ( &coord, &ll );
1175 if ( vdl->source == DEM_SOURCE_SRTM )
1176 dem_file = srtm_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1177 #ifdef VIK_CONFIG_DEM24K
1178 else if ( vdl->source == DEM_SOURCE_DEM24K )
1179 dem_file = dem24k_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1185 full_path = g_strdup_printf("%s%s", MAPS_CACHE_DIR, dem_file );
1187 g_debug("%s: %s", __FUNCTION__, full_path);
1189 // TODO: check if already in filelist
1191 if ( ! dem_layer_add_file(vdl, full_path) ) {
1192 gchar *tmp = g_strdup_printf ( _("Downloading DEM %s"), dem_file );
1193 DEMDownloadParams *p = g_malloc(sizeof(DEMDownloadParams));
1194 p->dest = g_strdup(full_path);
1198 p->mutex = g_mutex_new();
1199 p->source = vdl->source;
1200 g_object_weak_ref(G_OBJECT(p->vdl), weak_ref_cb, p );
1202 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vdl), tmp,
1203 (vik_thr_func) dem_download_thread, p,
1204 (vik_thr_free_func) free_dem_download_params, NULL, 1 );
1209 vik_layer_emit_update ( VIK_LAYER(vdl) );
1211 g_free ( dem_file );
1212 g_free ( full_path );
1217 static gboolean dem_layer_download_click ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1219 /* choose & keep track of cache dir
1220 * download in background thread
1221 * download over area */