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 void dem_layer_marshall( VikDEMLayer *vdl, guint8 **data, gint *len );
75 static VikDEMLayer *dem_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
76 static gboolean dem_layer_set_param ( VikDEMLayer *vdl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
77 static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gboolean is_file_operation );
78 static void dem_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file );
79 static void srtm_draw_existence ( VikViewport *vp );
81 #ifdef VIK_CONFIG_DEM24K
82 static void dem24k_draw_existence ( VikViewport *vp );
85 /* Upped upper limit incase units are feet */
86 static VikLayerParamScale param_scales[] = {
91 static gchar *params_source[] = {
92 "SRTM Global 90m (3 arcsec)",
93 #ifdef VIK_CONFIG_DEM24K
100 static gchar *params_type[] = {
101 N_("Absolute height"),
102 N_("Height gradient"),
106 enum { DEM_SOURCE_SRTM,
107 #ifdef VIK_CONFIG_DEM24K
113 enum { DEM_TYPE_HEIGHT = 0,
118 static VikLayerParam dem_layer_params[] = {
119 { "files", VIK_LAYER_PARAM_STRING_LIST, VIK_LAYER_GROUP_NONE, N_("DEM Files:"), VIK_LAYER_WIDGET_FILELIST },
120 { "source", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Download Source:"), VIK_LAYER_WIDGET_RADIOGROUP_STATIC, params_source, NULL },
121 { "color", VIK_LAYER_PARAM_COLOR, VIK_LAYER_GROUP_NONE, N_("Min Elev Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
122 { "type", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Type:"), VIK_LAYER_WIDGET_RADIOGROUP_STATIC, params_type, NULL },
123 { "min_elev", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_GROUP_NONE, N_("Min Elev:"), VIK_LAYER_WIDGET_SPINBUTTON, param_scales + 0 },
124 { "max_elev", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_GROUP_NONE, N_("Max Elev:"), VIK_LAYER_WIDGET_SPINBUTTON, param_scales + 0 },
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 { N_("DEM Download/Import"), (VikToolConstructorFunc) dem_layer_download_create, NULL, NULL, NULL,
136 (VikToolMouseFunc) dem_layer_download_click, NULL, (VikToolMouseFunc) dem_layer_download_release,
137 (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_demdl_pixbuf },
142 The first entry is blue for a default 'sea' colour,
143 however the value used by the corresponding gc can be configured as part of the DEM layer properties.
144 The other colours, shaded from brown to white are used to give an indication of height.
146 static gchar *dem_height_colors[] = {
148 "#9b793c", "#9c7d40", "#9d8144", "#9e8549", "#9f894d", "#a08d51", "#a29156", "#a3955a", "#a4995e", "#a69d63",
149 "#a89f65", "#aaa267", "#ada569", "#afa76b", "#b1aa6d", "#b4ad6f", "#b6b071", "#b9b373", "#bcb676", "#beb978",
150 "#c0bc7a", "#c2c07d", "#c4c37f", "#c6c681", "#c8ca84", "#cacd86", "#ccd188", "#cfd58b", "#c2ce84", "#b5c87e",
151 "#a9c278", "#9cbb71", "#8fb56b", "#83af65", "#76a95e", "#6aa358", "#5e9d52", "#63a055", "#69a458", "#6fa85c",
152 "#74ac5f", "#7ab063", "#80b467", "#86b86a", "#8cbc6e", "#92c072", "#94c175", "#97c278", "#9ac47c", "#9cc57f",
153 "#9fc682", "#a2c886", "#a4c989", "#a7cb8d", "#aacd91", "#afce99", "#b5d0a1", "#bbd2aa", "#c0d3b2", "#c6d5ba",
154 "#ccd7c3", "#d1d9cb", "#d7dbd4", "#DDDDDD", "#e0e0e0", "#e4e4e4", "#e8e8e8", "#ebebeb", "#efefef", "#f3f3f3",
155 "#f7f7f7", "#fbfbfb", "#ffffff"
158 static const guint DEM_N_HEIGHT_COLORS = sizeof(dem_height_colors)/sizeof(dem_height_colors[0]);
161 "#9b793c", "#9e8549", "#a29156", "#a69d63", "#ada569", "#b4ad6f", "#bcb676", "#c2c07d", "#c8ca84", "#cfd58b",
162 "#a9c278", "#83af65", "#5e9d52", "#6fa85c", "#80b467", "#92c072", "#9ac47c", "#a2c886", "#aacd91", "#bbd2aa",
163 "#ccd7c3", "#DDDDDD", "#e8e8e8", "#f3f3f3", "#FFFFFF"
167 static gchar *dem_gradient_colors[] = {
169 "#000000", "#000011", "#000022", "#000033", "#000044", "#00004c", "#000055", "#00005d", "#000066", "#00006e",
170 "#000077", "#00007f", "#000088", "#000090", "#000099", "#0000a1", "#0000aa", "#0000b2", "#0000bb", "#0000c3",
171 "#0000cc", "#0000d4", "#0000dd", "#0000e5", "#0000ee", "#0000f6", "#0000ff", "#0008f7", "#0011ee", "#0019e6",
172 "#0022dd", "#002ad5", "#0033cc", "#003bc4", "#0044bb", "#004cb3", "#0055aa", "#005da2", "#006699", "#006e91",
173 "#007788", "#007f80", "#008877", "#00906f", "#009966", "#00a15e", "#00aa55", "#00b24d", "#00bb44", "#00c33c",
174 "#00cc33", "#00d42b", "#00dd22", "#00e51a", "#00ee11", "#00f609", "#00ff00", "#08f700", "#11ee00", "#19e600",
175 "#22dd00", "#2ad500", "#33cc00", "#3bc400", "#44bb00", "#4cb300", "#55aa00", "#5da200", "#669900", "#6e9100",
176 "#778800", "#7f8000", "#887700", "#906f00", "#996600", "#a15e00", "#aa5500", "#b24d00", "#bb4400", "#c33c00",
177 "#cc3300", "#d42b00", "#dd2200", "#e51a00", "#ee1100", "#f60900", "#ff0000",
181 static const guint DEM_N_GRADIENT_COLORS = sizeof(dem_gradient_colors)/sizeof(dem_gradient_colors[0]);
184 VikLayerInterface vik_dem_layer_interface = {
189 sizeof(dem_tools) / sizeof(dem_tools[0]),
198 (VikLayerFuncCreate) vik_dem_layer_create,
199 (VikLayerFuncRealize) NULL,
200 (VikLayerFuncPostRead) dem_layer_post_read,
201 (VikLayerFuncFree) vik_dem_layer_free,
203 (VikLayerFuncProperties) NULL,
204 (VikLayerFuncDraw) vik_dem_layer_draw,
205 (VikLayerFuncChangeCoordMode) NULL,
207 (VikLayerFuncSetMenuItemsSelection) NULL,
208 (VikLayerFuncGetMenuItemsSelection) NULL,
210 (VikLayerFuncAddMenuItems) NULL,
211 (VikLayerFuncSublayerAddMenuItems) NULL,
213 (VikLayerFuncSublayerRenameRequest) NULL,
214 (VikLayerFuncSublayerToggleVisible) NULL,
216 (VikLayerFuncMarshall) dem_layer_marshall,
217 (VikLayerFuncUnmarshall) dem_layer_unmarshall,
219 (VikLayerFuncSetParam) dem_layer_set_param,
220 (VikLayerFuncGetParam) dem_layer_get_param,
222 (VikLayerFuncReadFileData) NULL,
223 (VikLayerFuncWriteFileData) NULL,
225 (VikLayerFuncDeleteItem) NULL,
226 (VikLayerFuncCopyItem) NULL,
227 (VikLayerFuncPasteItem) NULL,
228 (VikLayerFuncFreeCopiedItem) NULL,
229 (VikLayerFuncDragDropRequest) NULL,
232 struct _VikDEMLayer {
244 GType vik_dem_layer_get_type ()
246 static GType vdl_type = 0;
250 static const GTypeInfo vdl_info =
252 sizeof (VikDEMLayerClass),
253 NULL, /* base_init */
254 NULL, /* base_finalize */
255 NULL, /* class init */
256 NULL, /* class_finalize */
257 NULL, /* class_data */
258 sizeof (VikDEMLayer),
260 NULL /* instance init */
262 vdl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikDEMLayer", &vdl_info, 0 );
268 static void dem_layer_marshall( VikDEMLayer *vdl, guint8 **data, gint *len )
270 vik_layer_marshall_params ( VIK_LAYER(vdl), data, len );
273 static VikDEMLayer *dem_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
275 VikDEMLayer *rv = vik_dem_layer_new ();
278 /* TODO: share GCS between layers */
279 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
280 rv->gcs[i] = vik_viewport_new_gc ( vvp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
282 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
283 rv->gcsgradient[i] = vik_viewport_new_gc ( vvp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
285 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
289 /* Structure for DEM data used in background thread */
292 } dem_load_thread_data;
295 * Function for starting the DEM file loading as a background thread
297 static int dem_layer_load_list_thread ( dem_load_thread_data *dltd, gpointer threaddata )
299 int result = 0; // Default to good
301 if ( a_dems_load_list ( &(dltd->vdl->files), threaddata ) ) {
306 // ATM as each file is processed the screen is not updated (no mechanism exposed to a_dems_load_list)
307 // Thus force draw only at the end, as loading is complete/aborted
309 vik_layer_emit_update ( VIK_LAYER(dltd->vdl) );
315 static void dem_layer_thread_data_free ( dem_load_thread_data *data )
321 static void dem_layer_thread_cancel ( dem_load_thread_data *data )
324 // Instead of freeing the list, leave it as partially processed
325 // Thus we can see/use what was done
328 gboolean dem_layer_set_param ( VikDEMLayer *vdl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
332 case PARAM_COLOR: gdk_gc_set_rgb_fg_color ( vdl->gcs[0], &(data.c) ); break;
333 case PARAM_SOURCE: vdl->source = data.u; break;
334 case PARAM_TYPE: vdl->type = data.u; break;
336 /* Convert to store internally
337 NB file operation always in internal units (metres) */
338 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
339 vdl->min_elev = VIK_FEET_TO_METERS(data.d);
341 vdl->min_elev = data.d;
344 /* Convert to store internally
345 NB file operation always in internal units (metres) */
346 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
347 vdl->max_elev = VIK_FEET_TO_METERS(data.d);
349 vdl->max_elev = data.d;
353 // Clear out old settings - if any commonalities with new settings they will have to be read again
354 a_dems_list_free ( vdl->files );
355 // Set file list so any other intermediate screen drawing updates will show currently loaded DEMs by the working thread
356 vdl->files = data.sl;
358 dem_load_thread_data *dltd = g_malloc ( sizeof(dem_load_thread_data) );
360 dltd->vdl->files = data.sl;
362 a_background_thread ( VIK_GTK_WINDOW_FROM_WIDGET(vp),
364 (vik_thr_func) dem_layer_load_list_thread,
366 (vik_thr_free_func) dem_layer_thread_data_free,
367 (vik_thr_free_func) dem_layer_thread_cancel,
368 g_list_length ( data.sl ) ); // Number of DEM files
375 static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gboolean is_file_operation )
377 VikLayerParamData rv;
380 case PARAM_FILES: rv.sl = vdl->files; break;
381 case PARAM_SOURCE: rv.u = vdl->source; break;
382 case PARAM_TYPE: rv.u = vdl->type; break;
383 case PARAM_COLOR: vik_gc_get_fg_color ( vdl->gcs[0], &(rv.c) ); break;
385 /* Convert for display in desired units
386 NB file operation always in internal units (metres) */
387 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
388 rv.d = VIK_METERS_TO_FEET(vdl->min_elev);
390 rv.d = vdl->min_elev;
393 /* Convert for display in desired units
394 NB file operation always in internal units (metres) */
395 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
396 rv.d = VIK_METERS_TO_FEET(vdl->max_elev);
398 rv.d = vdl->max_elev;
404 static void dem_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file )
406 /* nothing ATM, but keep in case it's needed the future */
409 VikDEMLayer *vik_dem_layer_new ( )
411 VikDEMLayer *vdl = VIK_DEM_LAYER ( g_object_new ( VIK_DEM_LAYER_TYPE, NULL ) );
413 vik_layer_init ( VIK_LAYER(vdl), VIK_LAYER_DEM );
417 vdl->gcs = g_malloc(sizeof(GdkGC *)*DEM_N_HEIGHT_COLORS);
418 vdl->gcsgradient = g_malloc(sizeof(GdkGC *)*DEM_N_GRADIENT_COLORS);
419 /* make new gcs only if we need it (copy layer -> use old) */
422 vdl->max_elev = 1000.0;
423 vdl->source = DEM_SOURCE_SRTM;
424 vdl->type = DEM_TYPE_HEIGHT;
429 static inline guint16 get_height_difference(gint16 elev, gint16 new_elev)
431 if(new_elev == VIK_DEM_INVALID_ELEVATION)
434 return abs(new_elev - elev);
438 static void vik_dem_layer_draw_dem ( VikDEMLayer *vdl, VikViewport *vp, VikDEM *dem )
440 VikDEMColumn *column, *prevcolumn, *nextcolumn;
442 struct LatLon dem_northeast, dem_southwest;
443 gdouble max_lat, max_lon, min_lat, min_lon;
445 /**** Check if viewport and DEM data overlap ****/
447 /* get min, max lat/lon of viewport */
448 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
450 /* get min, max lat/lon of DEM data */
451 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
452 dem_northeast.lat = dem->max_north / 3600.0;
453 dem_northeast.lon = dem->max_east / 3600.0;
454 dem_southwest.lat = dem->min_north / 3600.0;
455 dem_southwest.lon = dem->min_east / 3600.0;
456 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
457 struct UTM dem_northeast_utm, dem_southwest_utm;
458 dem_northeast_utm.northing = dem->max_north;
459 dem_northeast_utm.easting = dem->max_east;
460 dem_southwest_utm.northing = dem->min_north;
461 dem_southwest_utm.easting = dem->min_east;
462 dem_northeast_utm.zone = dem_southwest_utm.zone = dem->utm_zone;
463 dem_northeast_utm.letter = dem_southwest_utm.letter = dem->utm_letter;
465 a_coords_utm_to_latlon(&dem_northeast_utm, &dem_northeast);
466 a_coords_utm_to_latlon(&dem_southwest_utm, &dem_southwest);
469 if ( (max_lat > dem_northeast.lat && min_lat > dem_northeast.lat) ||
470 (max_lat < dem_southwest.lat && min_lat < dem_southwest.lat) )
472 else if ( (max_lon > dem_northeast.lon && min_lon > dem_northeast.lon) ||
473 (max_lon < dem_southwest.lon && min_lon < dem_southwest.lon) )
475 /* else they overlap */
477 /**** End Overlap Check ****/
478 /* boxes to show where we have DEM instead of actually drawing the DEM.
479 * useful if we want to see what areas we have coverage for (if we want
480 * to get elevation data for a track) but don't want to cover the map.
484 /* draw a box if a DEM is loaded. in future I'd like to add an option for this
485 * this is useful if we want to see what areas we have dem for but don't want to
486 * cover the map (or maybe we just need translucent DEM?) */
488 VikCoord demne, demsw;
490 vik_coord_load_from_latlon(&demne, vik_viewport_get_coord_mode(vp), &dem_northeast);
491 vik_coord_load_from_latlon(&demsw, vik_viewport_get_coord_mode(vp), &dem_southwest);
493 vik_viewport_coord_to_screen ( vp, &demne, &x1, &y1 );
494 vik_viewport_coord_to_screen ( vp, &demsw, &x2, &y2 );
496 if ( x1 > vik_viewport_get_width(vp) ) x1=vik_viewport_get_width(vp);
497 if ( y2 > vik_viewport_get_height(vp) ) y2=vik_viewport_get_height(vp);
498 if ( x2 < 0 ) x2 = 0;
499 if ( y1 < 0 ) y1 = 0;
500 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
501 FALSE, x2, y1, x1-x2, y2-y1 );
506 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
507 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
509 gdouble max_lat_as, max_lon_as, min_lat_as, min_lon_as;
510 gdouble start_lat_as, end_lat_as, start_lon_as, end_lon_as;
512 gdouble start_lat, end_lat, start_lon, end_lon;
514 struct LatLon counter;
516 guint x, y, start_x, start_y;
520 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 80 ); /* todo: smarter calculation. */
522 gdouble nscale_deg = dem->north_scale / ((gdouble) 3600);
523 gdouble escale_deg = dem->east_scale / ((gdouble) 3600);
525 max_lat_as = max_lat * 3600;
526 min_lat_as = min_lat * 3600;
527 max_lon_as = max_lon * 3600;
528 min_lon_as = min_lon * 3600;
530 start_lat_as = MAX(min_lat_as, dem->min_north);
531 end_lat_as = MIN(max_lat_as, dem->max_north);
532 start_lon_as = MAX(min_lon_as, dem->min_east);
533 end_lon_as = MIN(max_lon_as, dem->max_east);
535 start_lat = floor(start_lat_as / dem->north_scale) * nscale_deg;
536 end_lat = ceil (end_lat_as / dem->north_scale) * nscale_deg;
537 start_lon = floor(start_lon_as / dem->east_scale) * escale_deg;
538 end_lon = ceil (end_lon_as / dem->east_scale) * escale_deg;
540 vik_dem_east_north_to_xy ( dem, start_lon_as, start_lat_as, &start_x, &start_y );
541 guint gradient_skip_factor = 1;
542 if(vdl->type == DEM_TYPE_GRADIENT)
543 gradient_skip_factor = skip_factor;
545 /* verify sane elev interval */
546 if ( vdl->max_elev <= vdl->min_elev )
547 vdl->max_elev = vdl->min_elev + 1;
549 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 ) {
550 // NOTE: ( counter.lon <= end_lon + ESCALE_DEG*SKIP_FACTOR ) is neccessary so in high zoom modes,
551 // the leftmost column does also get drawn, if the center point is out of viewport.
552 if ( x >= 0 && x < dem->n_columns ) {
553 column = g_ptr_array_index ( dem->columns, x );
554 // get previous and next column. catch out-of-bound.
556 new_x -= gradient_skip_factor;
558 prevcolumn = g_ptr_array_index ( dem->columns, x+1);
560 prevcolumn = g_ptr_array_index ( dem->columns, new_x);
562 new_x += gradient_skip_factor;
563 if(new_x >= dem->n_columns)
564 nextcolumn = g_ptr_array_index ( dem->columns, x-1);
566 nextcolumn = g_ptr_array_index ( dem->columns, new_x);
568 for ( y=start_y, counter.lat = start_lat; counter.lat <= end_lat; counter.lat += nscale_deg * skip_factor, y += skip_factor ) {
569 if ( y > column->n_points )
572 elev = column->points[y];
574 // calculate bounding box for drawing
575 gint box_x, box_y, box_width, box_height;
578 box_c.lat += (nscale_deg * skip_factor)/2;
579 box_c.lon -= (escale_deg * skip_factor)/2;
580 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
581 vik_viewport_coord_to_screen(vp, &tmp, &box_x, &box_y);
582 // catch box at borders
587 box_c.lat -= nscale_deg * skip_factor;
588 box_c.lon += escale_deg * skip_factor;
589 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
590 vik_viewport_coord_to_screen(vp, &tmp, &box_width, &box_height);
593 // catch box at borders
594 if(box_width < 0 || box_height < 0)
595 continue; // skip this. this is out of our viewport anyway. FIXME: why?
597 gboolean below_minimum = FALSE;
598 if(vdl->type == DEM_TYPE_HEIGHT) {
599 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev ) {
600 // Prevent 'elev - vdl->min_elev' from being negative so can safely use as array index
601 elev = ceil ( vdl->min_elev );
602 below_minimum = TRUE;
604 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
605 elev = vdl->max_elev;
609 if(box_width < 0 || box_height < 0) // FIXME: why does this happen?
612 if(vdl->type == DEM_TYPE_GRADIENT) {
613 if( elev == VIK_DEM_INVALID_ELEVATION ) {
616 // calculate and sum gradient in all directions
620 // calculate gradient from height points all around the current one
621 new_y = y - gradient_skip_factor;
624 change += get_height_difference(elev, prevcolumn->points[new_y]);
625 change += get_height_difference(elev, column->points[new_y]);
626 change += get_height_difference(elev, nextcolumn->points[new_y]);
628 change += get_height_difference(elev, prevcolumn->points[y]);
629 change += get_height_difference(elev, nextcolumn->points[y]);
631 new_y = y + gradient_skip_factor;
632 if(new_y >= column->n_points)
634 change += get_height_difference(elev, prevcolumn->points[new_y]);
635 change += get_height_difference(elev, column->points[new_y]);
636 change += get_height_difference(elev, nextcolumn->points[new_y]);
638 change = change / ((skip_factor > 1) ? log(skip_factor) : 0.55); // FIXME: better calc.
640 if(change < vdl->min_elev)
641 // Prevent 'change - vdl->min_elev' from being negative so can safely use as array index
642 change = ceil ( vdl->min_elev );
644 if(change > vdl->max_elev)
645 change = vdl->max_elev;
647 // void vik_viewport_draw_rectangle ( VikViewport *vvp, GdkGC *gc, gboolean filled, gint x1, gint y1, gint x2, gint y2 );
648 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);
651 if(vdl->type == DEM_TYPE_HEIGHT) {
652 if ( elev == VIK_DEM_INVALID_ELEVATION )
653 ; /* don't draw it */
654 else if ( elev <= 0 || below_minimum )
655 /* If 'sea' colour or below the defined mininum draw in the configurable colour */
656 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, box_x, box_y, box_width, box_height);
658 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);
665 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
666 gdouble max_nor, max_eas, min_nor, min_eas;
667 gdouble start_nor, start_eas, end_nor, end_eas;
671 guint x, y, start_x, start_y;
673 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
676 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 10 ); /* todo: smarter calculation. */
678 VikCoord tleft, tright, bleft, bright;
680 vik_viewport_screen_to_coord ( vp, 0, 0, &tleft );
681 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), 0, &tright );
682 vik_viewport_screen_to_coord ( vp, 0, vik_viewport_get_height(vp), &bleft );
683 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), vik_viewport_get_height(vp), &bright );
686 vik_coord_convert(&tleft, VIK_COORD_UTM);
687 vik_coord_convert(&tright, VIK_COORD_UTM);
688 vik_coord_convert(&bleft, VIK_COORD_UTM);
689 vik_coord_convert(&bright, VIK_COORD_UTM);
691 max_nor = MAX(tleft.north_south, tright.north_south);
692 min_nor = MIN(bleft.north_south, bright.north_south);
693 max_eas = MAX(bright.east_west, tright.east_west);
694 min_eas = MIN(bleft.east_west, tleft.east_west);
696 start_nor = MAX(min_nor, dem->min_north);
697 end_nor = MIN(max_nor, dem->max_north);
698 if ( tleft.utm_zone == dem->utm_zone && bleft.utm_zone == dem->utm_zone
699 && (tleft.utm_letter >= 'N') == (dem->utm_letter >= 'N')
700 && (bleft.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
701 start_eas = MAX(min_eas, dem->min_east);
703 start_eas = dem->min_east;
704 if ( tright.utm_zone == dem->utm_zone && bright.utm_zone == dem->utm_zone
705 && (tright.utm_letter >= 'N') == (dem->utm_letter >= 'N')
706 && (bright.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
707 end_eas = MIN(max_eas, dem->max_east);
709 end_eas = dem->max_east;
711 start_nor = floor(start_nor / dem->north_scale) * dem->north_scale;
712 end_nor = ceil (end_nor / dem->north_scale) * dem->north_scale;
713 start_eas = floor(start_eas / dem->east_scale) * dem->east_scale;
714 end_eas = ceil (end_eas / dem->east_scale) * dem->east_scale;
716 vik_dem_east_north_to_xy ( dem, start_eas, start_nor, &start_x, &start_y );
718 /* TODO: why start_x and start_y are -1 -- rounding error from above? */
720 counter.zone = dem->utm_zone;
721 counter.letter = dem->utm_letter;
723 for ( x=start_x, counter.easting = start_eas; counter.easting <= end_eas; counter.easting += dem->east_scale * skip_factor, x += skip_factor ) {
724 if ( x > 0 && x < dem->n_columns ) {
725 column = g_ptr_array_index ( dem->columns, x );
726 for ( y=start_y, counter.northing = start_nor; counter.northing <= end_nor; counter.northing += dem->north_scale * skip_factor, y += skip_factor ) {
727 if ( y > column->n_points )
729 elev = column->points[y];
730 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev )
732 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
737 vik_coord_load_from_utm(&tmp, vik_viewport_get_coord_mode(vp), &counter);
738 vik_viewport_coord_to_screen(vp, &tmp, &a, &b);
739 if ( elev == VIK_DEM_INVALID_ELEVATION )
740 ; /* don't draw it */
741 else if ( elev <= 0 )
742 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, a-1, b-1, 2, 2 );
744 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 );
752 /* return the continent for the specified lat, lon */
754 static const gchar *srtm_continent_dir ( gint lat, gint lon )
756 extern const char *_srtm_continent_data[];
757 static GHashTable *srtm_continent = NULL;
758 const gchar *continent;
761 if (!srtm_continent) {
764 srtm_continent = g_hash_table_new(g_str_hash, g_str_equal);
765 s = _srtm_continent_data;
766 while (*s != (gchar *)-1) {
769 g_hash_table_insert(srtm_continent, (gpointer) *s, (gpointer) continent);
775 g_snprintf(name, sizeof(name), "%c%02d%c%03d",
776 (lat >= 0) ? 'N' : 'S', ABS(lat),
777 (lon >= 0) ? 'E' : 'W', ABS(lon));
779 return(g_hash_table_lookup(srtm_continent, name));
782 void vik_dem_layer_draw ( VikDEMLayer *vdl, gpointer data )
784 VikViewport *vp = (VikViewport *) data;
785 GList *dems_iter = vdl->files;
789 /* search for SRTM3 90m */
791 if ( vdl->source == DEM_SOURCE_SRTM )
792 srtm_draw_existence ( vp );
793 #ifdef VIK_CONFIG_DEM24K
794 else if ( vdl->source == DEM_SOURCE_DEM24K )
795 dem24k_draw_existence ( vp );
798 while ( dems_iter ) {
799 dem = a_dems_get ( (const char *) (dems_iter->data) );
801 vik_dem_layer_draw_dem ( vdl, vp, dem );
802 dems_iter = dems_iter->next;
806 void vik_dem_layer_free ( VikDEMLayer *vdl )
809 if ( vdl->color != NULL )
810 g_object_unref ( vdl->color );
813 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
814 g_object_unref ( vdl->gcs[i] );
817 if ( vdl->gcsgradient )
818 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
819 g_object_unref ( vdl->gcsgradient[i] );
820 g_free ( vdl->gcsgradient );
822 a_dems_list_free ( vdl->files );
825 VikDEMLayer *vik_dem_layer_create ( VikViewport *vp )
827 VikDEMLayer *vdl = vik_dem_layer_new ();
830 /* TODO: share GCS between layers */
831 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
832 vdl->gcs[i] = vik_viewport_new_gc ( vp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
834 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
835 vdl->gcsgradient[i] = vik_viewport_new_gc ( vp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
839 /**************************************************************
840 **** SOURCES & DOWNLOADING
841 **************************************************************/
847 VikDEMLayer *vdl; /* NULL if not alive */
853 /**************************************************
855 **************************************************/
857 static void srtm_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
860 const gchar *continent_dir;
862 intlat = (int)floor(p->lat);
863 intlon = (int)floor(p->lon);
864 continent_dir = srtm_continent_dir(intlat, intlon);
866 if (!continent_dir) {
867 g_warning(N_("No SRTM data available for %f, %f"), p->lat, p->lon);
871 gchar *src_fn = g_strdup_printf("%s%s/%c%02d%c%03d.hgt.zip",
874 (intlat >= 0) ? 'N' : 'S',
876 (intlon >= 0) ? 'E' : 'W',
879 static DownloadMapOptions options = { FALSE, FALSE, NULL, 0, a_check_map_file };
880 a_http_download_get_url ( SRTM_HTTP_SITE, src_fn, p->dest, &options, NULL );
884 static gchar *srtm_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
887 const gchar *continent_dir;
889 intlat = (int)floor(lat);
890 intlon = (int)floor(lon);
891 continent_dir = srtm_continent_dir(intlat, intlon);
894 continent_dir = "nowhere";
896 return g_strdup_printf("srtm3-%s%s%c%02d%c%03d.hgt.zip",
899 (intlat >= 0) ? 'N' : 'S',
901 (intlon >= 0) ? 'E' : 'W',
906 /* TODO: generalize */
907 static void srtm_draw_existence ( VikViewport *vp )
909 gdouble max_lat, max_lon, min_lat, min_lon;
910 gchar buf[strlen(MAPS_CACHE_DIR)+strlen(SRTM_CACHE_TEMPLATE)+30];
913 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
915 for (i = floor(min_lat); i <= floor(max_lat); i++) {
916 for (j = floor(min_lon); j <= floor(max_lon); j++) {
917 const gchar *continent_dir;
918 if ((continent_dir = srtm_continent_dir(i, j)) == NULL)
920 g_snprintf(buf, sizeof(buf), SRTM_CACHE_TEMPLATE,
924 (i >= 0) ? 'N' : 'S',
926 (j >= 0) ? 'E' : 'W',
928 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
933 sw.mode = VIK_COORD_LATLON;
934 ne.north_south = i+1;
936 ne.mode = VIK_COORD_LATLON;
937 vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
938 vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
939 if ( x1 < 0 ) x1 = 0;
940 if ( y2 < 0 ) y2 = 0;
941 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
942 FALSE, x1, y2, x2-x1, y1-y2 );
949 /**************************************************
951 **************************************************/
953 #ifdef VIK_CONFIG_DEM24K
955 static void dem24k_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
958 gchar *cmdline = g_strdup_printf("%s %.03f %.03f",
959 DEM24K_DOWNLOAD_SCRIPT,
962 /* FIX: don't use system, use execv or something. check for existence */
966 static gchar *dem24k_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
968 return g_strdup_printf("dem24k/%d/%d/%.03f,%.03f.dem",
975 /* TODO: generalize */
976 static void dem24k_draw_existence ( VikViewport *vp )
978 gdouble max_lat, max_lon, min_lat, min_lon;
979 gchar buf[strlen(MAPS_CACHE_DIR)+40];
982 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
984 for (i = floor(min_lat*8)/8; i <= floor(max_lat*8)/8; i+=0.125) {
985 /* check lat dir first -- faster */
986 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/",
989 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
991 for (j = floor(min_lon*8)/8; j <= floor(max_lon*8)/8; j+=0.125) {
992 /* check lon dir first -- faster */
993 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/",
997 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
999 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/%.03f,%.03f.dem",
1005 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1007 gint x1, y1, x2, y2;
1009 sw.east_west = j-0.125;
1010 sw.mode = VIK_COORD_LATLON;
1011 ne.north_south = i+0.125;
1013 ne.mode = VIK_COORD_LATLON;
1014 vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
1015 vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
1016 if ( x1 < 0 ) x1 = 0;
1017 if ( y2 < 0 ) y2 = 0;
1018 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
1019 FALSE, x1, y2, x2-x1, y1-y2 );
1026 /**************************************************
1027 * SOURCES -- DOWNLOADING & IMPORTING TOOL *
1028 **************************************************
1031 static void weak_ref_cb ( gpointer ptr, GObject * dead_vdl )
1033 DEMDownloadParams *p = ptr;
1034 g_mutex_lock ( p->mutex );
1036 g_mutex_unlock ( p->mutex );
1039 /* Try to add file full_path.
1040 * full_path will be copied.
1041 * returns FALSE if file does not exists, TRUE otherwise.
1043 static gboolean dem_layer_add_file ( VikDEMLayer *vdl, const gchar *full_path )
1045 if ( g_file_test(full_path, G_FILE_TEST_EXISTS ) == TRUE ) {
1046 /* only load if file size is not 0 (not in progress) */
1048 stat (full_path, &sb);
1050 gchar *duped_path = g_strdup(full_path);
1051 vdl->files = g_list_prepend ( vdl->files, duped_path );
1052 a_dems_load ( duped_path );
1053 g_debug("%s: %s", __FUNCTION__, duped_path);
1054 vik_layer_emit_update ( VIK_LAYER(vdl) );
1061 static void dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
1063 if ( p->source == DEM_SOURCE_SRTM )
1064 srtm_dem_download_thread ( p, threaddata );
1065 #ifdef VIK_CONFIG_DEM24K
1066 else if ( p->source == DEM_SOURCE_DEM24K )
1067 dem24k_dem_download_thread ( p, threaddata );
1070 gdk_threads_enter();
1071 g_mutex_lock ( p->mutex );
1073 g_object_weak_unref ( G_OBJECT(p->vdl), weak_ref_cb, p );
1075 if ( dem_layer_add_file ( p->vdl, p->dest ) )
1076 vik_layer_emit_update ( VIK_LAYER(p->vdl) );
1078 g_mutex_unlock ( p->mutex );
1079 gdk_threads_leave();
1083 static void free_dem_download_params ( DEMDownloadParams *p )
1085 g_mutex_free ( p->mutex );
1090 static gpointer dem_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1096 static gboolean dem_layer_download_release ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1102 gchar *dem_file = NULL;
1104 if ( vdl->source == DEM_SOURCE_NONE )
1105 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vdl), _("No download source selected. Edit layer properties.") );
1107 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1108 vik_coord_to_latlon ( &coord, &ll );
1111 if ( vdl->source == DEM_SOURCE_SRTM )
1112 dem_file = srtm_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1113 #ifdef VIK_CONFIG_DEM24K
1114 else if ( vdl->source == DEM_SOURCE_DEM24K )
1115 dem_file = dem24k_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1121 full_path = g_strdup_printf("%s%s", MAPS_CACHE_DIR, dem_file );
1123 g_debug("%s: %s", __FUNCTION__, full_path);
1125 // TODO: check if already in filelist
1127 if ( ! dem_layer_add_file(vdl, full_path) ) {
1128 gchar *tmp = g_strdup_printf ( _("Downloading DEM %s"), dem_file );
1129 DEMDownloadParams *p = g_malloc(sizeof(DEMDownloadParams));
1130 p->dest = g_strdup(full_path);
1134 p->mutex = g_mutex_new();
1135 p->source = vdl->source;
1136 g_object_weak_ref(G_OBJECT(p->vdl), weak_ref_cb, p );
1138 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vdl), tmp,
1139 (vik_thr_func) dem_download_thread, p,
1140 (vik_thr_free_func) free_dem_download_params, NULL, 1 );
1145 g_free ( dem_file );
1146 g_free ( full_path );
1151 static gboolean dem_layer_download_click ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1153 /* choose & keep track of cache dir
1154 * download in background thread
1155 * download over area */