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>
45 #include "background.h"
46 #include "vikwaypoint.h"
48 #include "vikviewport.h"
49 #include "viktreeview.h"
51 #include "vikaggregatelayer.h"
52 #include "viklayerspanel.h"
53 #include "vikmapslayer.h"
54 #include "vikdemlayer.h"
60 #include "icons/icons.h"
62 #define MAPS_CACHE_DIR maps_layer_default_dir()
64 #define SRTM_CACHE_TEMPLATE "%ssrtm3-%s%s%c%02d%c%03d.hgt.zip"
65 #define SRTM_HTTP_SITE "dds.cr.usgs.gov"
66 #define SRTM_HTTP_URI "/srtm/version2_1/SRTM3/"
68 #ifdef VIK_CONFIG_DEM24K
69 #define DEM24K_DOWNLOAD_SCRIPT "dem24k.pl"
72 #define UNUSED_LINE_THICKNESS 3
74 static const gchar* dem_layer_tooltip( VikDEMLayer *vdl );
75 static void dem_layer_marshall( VikDEMLayer *vdl, guint8 **data, gint *len );
76 static VikDEMLayer *dem_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
77 static gboolean dem_layer_set_param ( VikDEMLayer *vdl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
78 static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gboolean is_file_operation );
79 static void dem_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file );
80 static void srtm_draw_existence ( VikViewport *vp );
82 #ifdef VIK_CONFIG_DEM24K
83 static void dem24k_draw_existence ( VikViewport *vp );
86 /* Upped upper limit incase units are feet */
87 static VikLayerParamScale param_scales[] = {
92 static gchar *params_source[] = {
93 "SRTM Global 90m (3 arcsec)",
94 #ifdef VIK_CONFIG_DEM24K
101 static gchar *params_type[] = {
102 N_("Absolute height"),
103 N_("Height gradient"),
107 enum { DEM_SOURCE_SRTM,
108 #ifdef VIK_CONFIG_DEM24K
114 enum { DEM_TYPE_HEIGHT = 0,
119 static VikLayerParam dem_layer_params[] = {
120 { "files", VIK_LAYER_PARAM_STRING_LIST, VIK_LAYER_GROUP_NONE, N_("DEM Files:"), VIK_LAYER_WIDGET_FILELIST },
121 { "source", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Download Source:"), VIK_LAYER_WIDGET_RADIOGROUP_STATIC, params_source, NULL },
122 { "color", VIK_LAYER_PARAM_COLOR, VIK_LAYER_GROUP_NONE, N_("Min Elev Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
123 { "type", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Type:"), VIK_LAYER_WIDGET_RADIOGROUP_STATIC, params_type, NULL },
124 { "min_elev", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_GROUP_NONE, N_("Min Elev:"), VIK_LAYER_WIDGET_SPINBUTTON, param_scales + 0 },
125 { "max_elev", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_GROUP_NONE, N_("Max Elev:"), VIK_LAYER_WIDGET_SPINBUTTON, param_scales + 0 },
129 enum { PARAM_FILES=0, PARAM_SOURCE, PARAM_COLOR, PARAM_TYPE, PARAM_MIN_ELEV, PARAM_MAX_ELEV, NUM_PARAMS };
131 static gpointer dem_layer_download_create ( VikWindow *vw, VikViewport *vvp);
132 static gboolean dem_layer_download_release ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp );
133 static gboolean dem_layer_download_click ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp );
135 static VikToolInterface dem_tools[] = {
136 { N_("DEM Download/Import"), (VikToolConstructorFunc) dem_layer_download_create, NULL, NULL, NULL,
137 (VikToolMouseFunc) dem_layer_download_click, NULL, (VikToolMouseFunc) dem_layer_download_release,
138 (VikToolKeyFunc) NULL, 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 = {
190 sizeof(dem_tools) / sizeof(dem_tools[0]),
199 (VikLayerFuncCreate) vik_dem_layer_create,
200 (VikLayerFuncRealize) NULL,
201 (VikLayerFuncPostRead) dem_layer_post_read,
202 (VikLayerFuncFree) vik_dem_layer_free,
204 (VikLayerFuncProperties) NULL,
205 (VikLayerFuncDraw) vik_dem_layer_draw,
206 (VikLayerFuncChangeCoordMode) NULL,
208 (VikLayerFuncSetMenuItemsSelection) NULL,
209 (VikLayerFuncGetMenuItemsSelection) NULL,
211 (VikLayerFuncAddMenuItems) NULL,
212 (VikLayerFuncSublayerAddMenuItems) NULL,
214 (VikLayerFuncSublayerRenameRequest) NULL,
215 (VikLayerFuncSublayerToggleVisible) NULL,
216 (VikLayerFuncSublayerTooltip) NULL,
217 (VikLayerFuncLayerTooltip) dem_layer_tooltip,
218 (VikLayerFuncLayerSelected) NULL,
220 (VikLayerFuncMarshall) dem_layer_marshall,
221 (VikLayerFuncUnmarshall) dem_layer_unmarshall,
223 (VikLayerFuncSetParam) dem_layer_set_param,
224 (VikLayerFuncGetParam) dem_layer_get_param,
226 (VikLayerFuncReadFileData) NULL,
227 (VikLayerFuncWriteFileData) NULL,
229 (VikLayerFuncDeleteItem) NULL,
230 (VikLayerFuncCutItem) NULL,
231 (VikLayerFuncCopyItem) NULL,
232 (VikLayerFuncPasteItem) NULL,
233 (VikLayerFuncFreeCopiedItem) NULL,
234 (VikLayerFuncDragDropRequest) NULL,
236 (VikLayerFuncSelectClick) NULL,
237 (VikLayerFuncSelectMove) NULL,
238 (VikLayerFuncSelectRelease) NULL,
239 (VikLayerFuncSelectedViewportMenu) NULL,
242 struct _VikDEMLayer {
254 GType vik_dem_layer_get_type ()
256 static GType vdl_type = 0;
260 static const GTypeInfo vdl_info =
262 sizeof (VikDEMLayerClass),
263 NULL, /* base_init */
264 NULL, /* base_finalize */
265 NULL, /* class init */
266 NULL, /* class_finalize */
267 NULL, /* class_data */
268 sizeof (VikDEMLayer),
270 NULL /* instance init */
272 vdl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikDEMLayer", &vdl_info, 0 );
278 static const gchar* dem_layer_tooltip( VikDEMLayer *vdl )
280 static gchar tmp_buf[100];
281 g_snprintf (tmp_buf, sizeof(tmp_buf), _("Number of files: %d"), g_list_length (vdl->files));
285 static void dem_layer_marshall( VikDEMLayer *vdl, guint8 **data, gint *len )
287 vik_layer_marshall_params ( VIK_LAYER(vdl), data, len );
290 static VikDEMLayer *dem_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
292 VikDEMLayer *rv = vik_dem_layer_new ();
295 /* TODO: share GCS between layers */
296 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
297 rv->gcs[i] = vik_viewport_new_gc ( vvp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
299 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
300 rv->gcsgradient[i] = vik_viewport_new_gc ( vvp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
302 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
306 /* Structure for DEM data used in background thread */
309 } dem_load_thread_data;
312 * Function for starting the DEM file loading as a background thread
314 static int dem_layer_load_list_thread ( dem_load_thread_data *dltd, gpointer threaddata )
316 int result = 0; // Default to good
318 if ( a_dems_load_list ( &(dltd->vdl->files), threaddata ) ) {
323 // ATM as each file is processed the screen is not updated (no mechanism exposed to a_dems_load_list)
324 // Thus force draw only at the end, as loading is complete/aborted
326 // Test is helpful to prevent Gtk-CRITICAL warnings if the program is exitted whilst loading
327 if ( IS_VIK_LAYER(dltd->vdl) )
328 vik_layer_emit_update ( VIK_LAYER(dltd->vdl) );
334 static void dem_layer_thread_data_free ( dem_load_thread_data *data )
340 static void dem_layer_thread_cancel ( dem_load_thread_data *data )
343 // Instead of freeing the list, leave it as partially processed
344 // Thus we can see/use what was done
347 gboolean dem_layer_set_param ( VikDEMLayer *vdl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
351 case PARAM_COLOR: gdk_gc_set_rgb_fg_color ( vdl->gcs[0], &(data.c) ); break;
352 case PARAM_SOURCE: vdl->source = data.u; break;
353 case PARAM_TYPE: vdl->type = data.u; break;
355 /* Convert to store internally
356 NB file operation always in internal units (metres) */
357 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
358 vdl->min_elev = VIK_FEET_TO_METERS(data.d);
360 vdl->min_elev = data.d;
363 /* Convert to store internally
364 NB file operation always in internal units (metres) */
365 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
366 vdl->max_elev = VIK_FEET_TO_METERS(data.d);
368 vdl->max_elev = data.d;
372 // Clear out old settings - if any commonalities with new settings they will have to be read again
373 a_dems_list_free ( vdl->files );
374 // Set file list so any other intermediate screen drawing updates will show currently loaded DEMs by the working thread
375 vdl->files = data.sl;
377 dem_load_thread_data *dltd = g_malloc ( sizeof(dem_load_thread_data) );
379 dltd->vdl->files = data.sl;
381 a_background_thread ( VIK_GTK_WINDOW_FROM_WIDGET(vp),
383 (vik_thr_func) dem_layer_load_list_thread,
385 (vik_thr_free_func) dem_layer_thread_data_free,
386 (vik_thr_free_func) dem_layer_thread_cancel,
387 g_list_length ( data.sl ) ); // Number of DEM files
394 static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gboolean is_file_operation )
396 VikLayerParamData rv;
399 case PARAM_FILES: rv.sl = vdl->files; break;
400 case PARAM_SOURCE: rv.u = vdl->source; break;
401 case PARAM_TYPE: rv.u = vdl->type; break;
402 case PARAM_COLOR: vik_gc_get_fg_color ( vdl->gcs[0], &(rv.c) ); break;
404 /* Convert for display in desired units
405 NB file operation always in internal units (metres) */
406 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
407 rv.d = VIK_METERS_TO_FEET(vdl->min_elev);
409 rv.d = vdl->min_elev;
412 /* Convert for display in desired units
413 NB file operation always in internal units (metres) */
414 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
415 rv.d = VIK_METERS_TO_FEET(vdl->max_elev);
417 rv.d = vdl->max_elev;
423 static void dem_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file )
425 /* nothing ATM, but keep in case it's needed the future */
428 VikDEMLayer *vik_dem_layer_new ( )
430 VikDEMLayer *vdl = VIK_DEM_LAYER ( g_object_new ( VIK_DEM_LAYER_TYPE, NULL ) );
432 vik_layer_init ( VIK_LAYER(vdl), VIK_LAYER_DEM );
436 vdl->gcs = g_malloc(sizeof(GdkGC *)*DEM_N_HEIGHT_COLORS);
437 vdl->gcsgradient = g_malloc(sizeof(GdkGC *)*DEM_N_GRADIENT_COLORS);
438 /* make new gcs only if we need it (copy layer -> use old) */
441 vdl->max_elev = 1000.0;
442 vdl->source = DEM_SOURCE_SRTM;
443 vdl->type = DEM_TYPE_HEIGHT;
448 static inline guint16 get_height_difference(gint16 elev, gint16 new_elev)
450 if(new_elev == VIK_DEM_INVALID_ELEVATION)
453 return abs(new_elev - elev);
457 static void vik_dem_layer_draw_dem ( VikDEMLayer *vdl, VikViewport *vp, VikDEM *dem )
459 VikDEMColumn *column, *prevcolumn, *nextcolumn;
461 struct LatLon dem_northeast, dem_southwest;
462 gdouble max_lat, max_lon, min_lat, min_lon;
464 /**** Check if viewport and DEM data overlap ****/
466 /* get min, max lat/lon of viewport */
467 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
469 /* get min, max lat/lon of DEM data */
470 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
471 dem_northeast.lat = dem->max_north / 3600.0;
472 dem_northeast.lon = dem->max_east / 3600.0;
473 dem_southwest.lat = dem->min_north / 3600.0;
474 dem_southwest.lon = dem->min_east / 3600.0;
475 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
476 struct UTM dem_northeast_utm, dem_southwest_utm;
477 dem_northeast_utm.northing = dem->max_north;
478 dem_northeast_utm.easting = dem->max_east;
479 dem_southwest_utm.northing = dem->min_north;
480 dem_southwest_utm.easting = dem->min_east;
481 dem_northeast_utm.zone = dem_southwest_utm.zone = dem->utm_zone;
482 dem_northeast_utm.letter = dem_southwest_utm.letter = dem->utm_letter;
484 a_coords_utm_to_latlon(&dem_northeast_utm, &dem_northeast);
485 a_coords_utm_to_latlon(&dem_southwest_utm, &dem_southwest);
488 if ( (max_lat > dem_northeast.lat && min_lat > dem_northeast.lat) ||
489 (max_lat < dem_southwest.lat && min_lat < dem_southwest.lat) )
491 else if ( (max_lon > dem_northeast.lon && min_lon > dem_northeast.lon) ||
492 (max_lon < dem_southwest.lon && min_lon < dem_southwest.lon) )
494 /* else they overlap */
496 /**** End Overlap Check ****/
497 /* boxes to show where we have DEM instead of actually drawing the DEM.
498 * useful if we want to see what areas we have coverage for (if we want
499 * to get elevation data for a track) but don't want to cover the map.
503 /* draw a box if a DEM is loaded. in future I'd like to add an option for this
504 * this is useful if we want to see what areas we have dem for but don't want to
505 * cover the map (or maybe we just need translucent DEM?) */
507 VikCoord demne, demsw;
509 vik_coord_load_from_latlon(&demne, vik_viewport_get_coord_mode(vp), &dem_northeast);
510 vik_coord_load_from_latlon(&demsw, vik_viewport_get_coord_mode(vp), &dem_southwest);
512 vik_viewport_coord_to_screen ( vp, &demne, &x1, &y1 );
513 vik_viewport_coord_to_screen ( vp, &demsw, &x2, &y2 );
515 if ( x1 > vik_viewport_get_width(vp) ) x1=vik_viewport_get_width(vp);
516 if ( y2 > vik_viewport_get_height(vp) ) y2=vik_viewport_get_height(vp);
517 if ( x2 < 0 ) x2 = 0;
518 if ( y1 < 0 ) y1 = 0;
519 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
520 FALSE, x2, y1, x1-x2, y2-y1 );
525 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
526 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
528 gdouble max_lat_as, max_lon_as, min_lat_as, min_lon_as;
529 gdouble start_lat_as, end_lat_as, start_lon_as, end_lon_as;
531 gdouble start_lat, end_lat, start_lon, end_lon;
533 struct LatLon counter;
535 guint x, y, start_x, start_y;
539 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 80 ); /* todo: smarter calculation. */
541 gdouble nscale_deg = dem->north_scale / ((gdouble) 3600);
542 gdouble escale_deg = dem->east_scale / ((gdouble) 3600);
544 max_lat_as = max_lat * 3600;
545 min_lat_as = min_lat * 3600;
546 max_lon_as = max_lon * 3600;
547 min_lon_as = min_lon * 3600;
549 start_lat_as = MAX(min_lat_as, dem->min_north);
550 end_lat_as = MIN(max_lat_as, dem->max_north);
551 start_lon_as = MAX(min_lon_as, dem->min_east);
552 end_lon_as = MIN(max_lon_as, dem->max_east);
554 start_lat = floor(start_lat_as / dem->north_scale) * nscale_deg;
555 end_lat = ceil (end_lat_as / dem->north_scale) * nscale_deg;
556 start_lon = floor(start_lon_as / dem->east_scale) * escale_deg;
557 end_lon = ceil (end_lon_as / dem->east_scale) * escale_deg;
559 vik_dem_east_north_to_xy ( dem, start_lon_as, start_lat_as, &start_x, &start_y );
560 guint gradient_skip_factor = 1;
561 if(vdl->type == DEM_TYPE_GRADIENT)
562 gradient_skip_factor = skip_factor;
564 /* verify sane elev interval */
565 if ( vdl->max_elev <= vdl->min_elev )
566 vdl->max_elev = vdl->min_elev + 1;
568 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 ) {
569 // NOTE: ( counter.lon <= end_lon + ESCALE_DEG*SKIP_FACTOR ) is neccessary so in high zoom modes,
570 // the leftmost column does also get drawn, if the center point is out of viewport.
571 if ( x >= 0 && x < dem->n_columns ) {
572 column = g_ptr_array_index ( dem->columns, x );
573 // get previous and next column. catch out-of-bound.
575 new_x -= gradient_skip_factor;
577 prevcolumn = g_ptr_array_index ( dem->columns, x+1);
579 prevcolumn = g_ptr_array_index ( dem->columns, new_x);
581 new_x += gradient_skip_factor;
582 if(new_x >= dem->n_columns)
583 nextcolumn = g_ptr_array_index ( dem->columns, x-1);
585 nextcolumn = g_ptr_array_index ( dem->columns, new_x);
587 for ( y=start_y, counter.lat = start_lat; counter.lat <= end_lat; counter.lat += nscale_deg * skip_factor, y += skip_factor ) {
588 if ( y > column->n_points )
591 elev = column->points[y];
593 // calculate bounding box for drawing
594 gint box_x, box_y, box_width, box_height;
597 box_c.lat += (nscale_deg * skip_factor)/2;
598 box_c.lon -= (escale_deg * skip_factor)/2;
599 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
600 vik_viewport_coord_to_screen(vp, &tmp, &box_x, &box_y);
601 // catch box at borders
606 box_c.lat -= nscale_deg * skip_factor;
607 box_c.lon += escale_deg * skip_factor;
608 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
609 vik_viewport_coord_to_screen(vp, &tmp, &box_width, &box_height);
612 // catch box at borders
613 if(box_width < 0 || box_height < 0)
614 continue; // skip this. this is out of our viewport anyway. FIXME: why?
616 gboolean below_minimum = FALSE;
617 if(vdl->type == DEM_TYPE_HEIGHT) {
618 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev ) {
619 // Prevent 'elev - vdl->min_elev' from being negative so can safely use as array index
620 elev = ceil ( vdl->min_elev );
621 below_minimum = TRUE;
623 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
624 elev = vdl->max_elev;
628 if(box_width < 0 || box_height < 0) // FIXME: why does this happen?
631 if(vdl->type == DEM_TYPE_GRADIENT) {
632 if( elev == VIK_DEM_INVALID_ELEVATION ) {
635 // calculate and sum gradient in all directions
639 // calculate gradient from height points all around the current one
640 new_y = y - gradient_skip_factor;
643 change += get_height_difference(elev, prevcolumn->points[new_y]);
644 change += get_height_difference(elev, column->points[new_y]);
645 change += get_height_difference(elev, nextcolumn->points[new_y]);
647 change += get_height_difference(elev, prevcolumn->points[y]);
648 change += get_height_difference(elev, nextcolumn->points[y]);
650 new_y = y + gradient_skip_factor;
651 if(new_y >= column->n_points)
653 change += get_height_difference(elev, prevcolumn->points[new_y]);
654 change += get_height_difference(elev, column->points[new_y]);
655 change += get_height_difference(elev, nextcolumn->points[new_y]);
657 change = change / ((skip_factor > 1) ? log(skip_factor) : 0.55); // FIXME: better calc.
659 if(change < vdl->min_elev)
660 // Prevent 'change - vdl->min_elev' from being negative so can safely use as array index
661 change = ceil ( vdl->min_elev );
663 if(change > vdl->max_elev)
664 change = vdl->max_elev;
666 // void vik_viewport_draw_rectangle ( VikViewport *vvp, GdkGC *gc, gboolean filled, gint x1, gint y1, gint x2, gint y2 );
667 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);
670 if(vdl->type == DEM_TYPE_HEIGHT) {
671 if ( elev == VIK_DEM_INVALID_ELEVATION )
672 ; /* don't draw it */
673 else if ( elev <= 0 || below_minimum )
674 /* If 'sea' colour or below the defined mininum draw in the configurable colour */
675 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, box_x, box_y, box_width, box_height);
677 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);
684 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
685 gdouble max_nor, max_eas, min_nor, min_eas;
686 gdouble start_nor, start_eas, end_nor, end_eas;
690 guint x, y, start_x, start_y;
692 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
695 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 10 ); /* todo: smarter calculation. */
697 VikCoord tleft, tright, bleft, bright;
699 vik_viewport_screen_to_coord ( vp, 0, 0, &tleft );
700 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), 0, &tright );
701 vik_viewport_screen_to_coord ( vp, 0, vik_viewport_get_height(vp), &bleft );
702 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), vik_viewport_get_height(vp), &bright );
705 vik_coord_convert(&tleft, VIK_COORD_UTM);
706 vik_coord_convert(&tright, VIK_COORD_UTM);
707 vik_coord_convert(&bleft, VIK_COORD_UTM);
708 vik_coord_convert(&bright, VIK_COORD_UTM);
710 max_nor = MAX(tleft.north_south, tright.north_south);
711 min_nor = MIN(bleft.north_south, bright.north_south);
712 max_eas = MAX(bright.east_west, tright.east_west);
713 min_eas = MIN(bleft.east_west, tleft.east_west);
715 start_nor = MAX(min_nor, dem->min_north);
716 end_nor = MIN(max_nor, dem->max_north);
717 if ( tleft.utm_zone == dem->utm_zone && bleft.utm_zone == dem->utm_zone
718 && (tleft.utm_letter >= 'N') == (dem->utm_letter >= 'N')
719 && (bleft.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
720 start_eas = MAX(min_eas, dem->min_east);
722 start_eas = dem->min_east;
723 if ( tright.utm_zone == dem->utm_zone && bright.utm_zone == dem->utm_zone
724 && (tright.utm_letter >= 'N') == (dem->utm_letter >= 'N')
725 && (bright.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
726 end_eas = MIN(max_eas, dem->max_east);
728 end_eas = dem->max_east;
730 start_nor = floor(start_nor / dem->north_scale) * dem->north_scale;
731 end_nor = ceil (end_nor / dem->north_scale) * dem->north_scale;
732 start_eas = floor(start_eas / dem->east_scale) * dem->east_scale;
733 end_eas = ceil (end_eas / dem->east_scale) * dem->east_scale;
735 vik_dem_east_north_to_xy ( dem, start_eas, start_nor, &start_x, &start_y );
737 /* TODO: why start_x and start_y are -1 -- rounding error from above? */
739 counter.zone = dem->utm_zone;
740 counter.letter = dem->utm_letter;
742 for ( x=start_x, counter.easting = start_eas; counter.easting <= end_eas; counter.easting += dem->east_scale * skip_factor, x += skip_factor ) {
743 if ( x > 0 && x < dem->n_columns ) {
744 column = g_ptr_array_index ( dem->columns, x );
745 for ( y=start_y, counter.northing = start_nor; counter.northing <= end_nor; counter.northing += dem->north_scale * skip_factor, y += skip_factor ) {
746 if ( y > column->n_points )
748 elev = column->points[y];
749 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev )
751 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
756 vik_coord_load_from_utm(&tmp, vik_viewport_get_coord_mode(vp), &counter);
757 vik_viewport_coord_to_screen(vp, &tmp, &a, &b);
758 if ( elev == VIK_DEM_INVALID_ELEVATION )
759 ; /* don't draw it */
760 else if ( elev <= 0 )
761 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, a-1, b-1, 2, 2 );
763 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 );
771 /* return the continent for the specified lat, lon */
773 static const gchar *srtm_continent_dir ( gint lat, gint lon )
775 extern const char *_srtm_continent_data[];
776 static GHashTable *srtm_continent = NULL;
777 const gchar *continent;
780 if (!srtm_continent) {
783 srtm_continent = g_hash_table_new(g_str_hash, g_str_equal);
784 s = _srtm_continent_data;
785 while (*s != (gchar *)-1) {
788 g_hash_table_insert(srtm_continent, (gpointer) *s, (gpointer) continent);
794 g_snprintf(name, sizeof(name), "%c%02d%c%03d",
795 (lat >= 0) ? 'N' : 'S', ABS(lat),
796 (lon >= 0) ? 'E' : 'W', ABS(lon));
798 return(g_hash_table_lookup(srtm_continent, name));
801 void vik_dem_layer_draw ( VikDEMLayer *vdl, gpointer data )
803 VikViewport *vp = (VikViewport *) data;
804 GList *dems_iter = vdl->files;
808 /* search for SRTM3 90m */
810 if ( vdl->source == DEM_SOURCE_SRTM )
811 srtm_draw_existence ( vp );
812 #ifdef VIK_CONFIG_DEM24K
813 else if ( vdl->source == DEM_SOURCE_DEM24K )
814 dem24k_draw_existence ( vp );
817 while ( dems_iter ) {
818 dem = a_dems_get ( (const char *) (dems_iter->data) );
820 vik_dem_layer_draw_dem ( vdl, vp, dem );
821 dems_iter = dems_iter->next;
825 void vik_dem_layer_free ( VikDEMLayer *vdl )
828 if ( vdl->color != NULL )
829 g_object_unref ( vdl->color );
832 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
833 g_object_unref ( vdl->gcs[i] );
836 if ( vdl->gcsgradient )
837 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
838 g_object_unref ( vdl->gcsgradient[i] );
839 g_free ( vdl->gcsgradient );
841 a_dems_list_free ( vdl->files );
844 VikDEMLayer *vik_dem_layer_create ( VikViewport *vp )
846 VikDEMLayer *vdl = vik_dem_layer_new ();
849 /* TODO: share GCS between layers */
850 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
851 vdl->gcs[i] = vik_viewport_new_gc ( vp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
853 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
854 vdl->gcsgradient[i] = vik_viewport_new_gc ( vp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
858 /**************************************************************
859 **** SOURCES & DOWNLOADING
860 **************************************************************/
866 VikDEMLayer *vdl; /* NULL if not alive */
872 /**************************************************
874 **************************************************/
876 static void srtm_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
879 const gchar *continent_dir;
881 intlat = (int)floor(p->lat);
882 intlon = (int)floor(p->lon);
883 continent_dir = srtm_continent_dir(intlat, intlon);
885 if (!continent_dir) {
886 g_warning(N_("No SRTM data available for %f, %f"), p->lat, p->lon);
890 gchar *src_fn = g_strdup_printf("%s%s/%c%02d%c%03d.hgt.zip",
893 (intlat >= 0) ? 'N' : 'S',
895 (intlon >= 0) ? 'E' : 'W',
898 static DownloadMapOptions options = { FALSE, FALSE, NULL, 0, a_check_map_file };
899 a_http_download_get_url ( SRTM_HTTP_SITE, src_fn, p->dest, &options, NULL );
903 static gchar *srtm_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
906 const gchar *continent_dir;
908 intlat = (int)floor(lat);
909 intlon = (int)floor(lon);
910 continent_dir = srtm_continent_dir(intlat, intlon);
913 continent_dir = "nowhere";
915 return g_strdup_printf("srtm3-%s%s%c%02d%c%03d.hgt.zip",
918 (intlat >= 0) ? 'N' : 'S',
920 (intlon >= 0) ? 'E' : 'W',
925 /* TODO: generalize */
926 static void srtm_draw_existence ( VikViewport *vp )
928 gdouble max_lat, max_lon, min_lat, min_lon;
929 gchar buf[strlen(MAPS_CACHE_DIR)+strlen(SRTM_CACHE_TEMPLATE)+30];
932 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
934 for (i = floor(min_lat); i <= floor(max_lat); i++) {
935 for (j = floor(min_lon); j <= floor(max_lon); j++) {
936 const gchar *continent_dir;
937 if ((continent_dir = srtm_continent_dir(i, j)) == NULL)
939 g_snprintf(buf, sizeof(buf), SRTM_CACHE_TEMPLATE,
943 (i >= 0) ? 'N' : 'S',
945 (j >= 0) ? 'E' : 'W',
947 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
952 sw.mode = VIK_COORD_LATLON;
953 ne.north_south = i+1;
955 ne.mode = VIK_COORD_LATLON;
956 vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
957 vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
958 if ( x1 < 0 ) x1 = 0;
959 if ( y2 < 0 ) y2 = 0;
960 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
961 FALSE, x1, y2, x2-x1, y1-y2 );
968 /**************************************************
970 **************************************************/
972 #ifdef VIK_CONFIG_DEM24K
974 static void dem24k_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
977 gchar *cmdline = g_strdup_printf("%s %.03f %.03f",
978 DEM24K_DOWNLOAD_SCRIPT,
981 /* FIX: don't use system, use execv or something. check for existence */
985 static gchar *dem24k_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
987 return g_strdup_printf("dem24k/%d/%d/%.03f,%.03f.dem",
994 /* TODO: generalize */
995 static void dem24k_draw_existence ( VikViewport *vp )
997 gdouble max_lat, max_lon, min_lat, min_lon;
998 gchar buf[strlen(MAPS_CACHE_DIR)+40];
1001 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
1003 for (i = floor(min_lat*8)/8; i <= floor(max_lat*8)/8; i+=0.125) {
1004 /* check lat dir first -- faster */
1005 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/",
1008 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1010 for (j = floor(min_lon*8)/8; j <= floor(max_lon*8)/8; j+=0.125) {
1011 /* check lon dir first -- faster */
1012 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/",
1016 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1018 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/%.03f,%.03f.dem",
1024 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1026 gint x1, y1, x2, y2;
1028 sw.east_west = j-0.125;
1029 sw.mode = VIK_COORD_LATLON;
1030 ne.north_south = i+0.125;
1032 ne.mode = VIK_COORD_LATLON;
1033 vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
1034 vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
1035 if ( x1 < 0 ) x1 = 0;
1036 if ( y2 < 0 ) y2 = 0;
1037 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
1038 FALSE, x1, y2, x2-x1, y1-y2 );
1045 /**************************************************
1046 * SOURCES -- DOWNLOADING & IMPORTING TOOL *
1047 **************************************************
1050 static void weak_ref_cb ( gpointer ptr, GObject * dead_vdl )
1052 DEMDownloadParams *p = ptr;
1053 g_mutex_lock ( p->mutex );
1055 g_mutex_unlock ( p->mutex );
1058 /* Try to add file full_path.
1059 * full_path will be copied.
1060 * returns FALSE if file does not exists, TRUE otherwise.
1062 static gboolean dem_layer_add_file ( VikDEMLayer *vdl, const gchar *full_path )
1064 if ( g_file_test(full_path, G_FILE_TEST_EXISTS ) == TRUE ) {
1065 /* only load if file size is not 0 (not in progress) */
1067 stat (full_path, &sb);
1069 gchar *duped_path = g_strdup(full_path);
1070 vdl->files = g_list_prepend ( vdl->files, duped_path );
1071 a_dems_load ( duped_path );
1072 g_debug("%s: %s", __FUNCTION__, duped_path);
1073 vik_layer_emit_update ( VIK_LAYER(vdl) );
1080 static void dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
1082 if ( p->source == DEM_SOURCE_SRTM )
1083 srtm_dem_download_thread ( p, threaddata );
1084 #ifdef VIK_CONFIG_DEM24K
1085 else if ( p->source == DEM_SOURCE_DEM24K )
1086 dem24k_dem_download_thread ( p, threaddata );
1089 gdk_threads_enter();
1090 g_mutex_lock ( p->mutex );
1092 g_object_weak_unref ( G_OBJECT(p->vdl), weak_ref_cb, p );
1094 if ( dem_layer_add_file ( p->vdl, p->dest ) )
1095 vik_layer_emit_update ( VIK_LAYER(p->vdl) );
1097 g_mutex_unlock ( p->mutex );
1098 gdk_threads_leave();
1102 static void free_dem_download_params ( DEMDownloadParams *p )
1104 g_mutex_free ( p->mutex );
1109 static gpointer dem_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1115 static gboolean dem_layer_download_release ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1121 gchar *dem_file = NULL;
1123 if ( vdl->source == DEM_SOURCE_NONE )
1124 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vdl), _("No download source selected. Edit layer properties.") );
1126 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1127 vik_coord_to_latlon ( &coord, &ll );
1130 if ( vdl->source == DEM_SOURCE_SRTM )
1131 dem_file = srtm_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1132 #ifdef VIK_CONFIG_DEM24K
1133 else if ( vdl->source == DEM_SOURCE_DEM24K )
1134 dem_file = dem24k_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1140 full_path = g_strdup_printf("%s%s", MAPS_CACHE_DIR, dem_file );
1142 g_debug("%s: %s", __FUNCTION__, full_path);
1144 // TODO: check if already in filelist
1146 if ( ! dem_layer_add_file(vdl, full_path) ) {
1147 gchar *tmp = g_strdup_printf ( _("Downloading DEM %s"), dem_file );
1148 DEMDownloadParams *p = g_malloc(sizeof(DEMDownloadParams));
1149 p->dest = g_strdup(full_path);
1153 p->mutex = g_mutex_new();
1154 p->source = vdl->source;
1155 g_object_weak_ref(G_OBJECT(p->vdl), weak_ref_cb, p );
1157 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vdl), tmp,
1158 (vik_thr_func) dem_download_thread, p,
1159 (vik_thr_free_func) free_dem_download_params, NULL, 1 );
1164 g_free ( dem_file );
1165 g_free ( full_path );
1170 static gboolean dem_layer_download_click ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1172 /* choose & keep track of cache dir
1173 * download in background thread
1174 * download over area */