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 gboolean dem_layer_set_param ( VikDEMLayer *vdl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
293 case PARAM_COLOR: gdk_gc_set_rgb_fg_color ( vdl->gcs[0], &(data.c) ); break;
294 case PARAM_SOURCE: vdl->source = data.u; break;
295 case PARAM_TYPE: vdl->type = data.u; break;
297 /* Convert to store internally
298 NB file operation always in internal units (metres) */
299 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
300 vdl->min_elev = VIK_FEET_TO_METERS(data.d);
302 vdl->min_elev = data.d;
305 /* Convert to store internally
306 NB file operation always in internal units (metres) */
307 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
308 vdl->max_elev = VIK_FEET_TO_METERS(data.d);
310 vdl->max_elev = data.d;
312 case PARAM_FILES: a_dems_load_list ( &(data.sl) ); a_dems_list_free ( vdl->files ); vdl->files = data.sl; break;
317 static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gboolean is_file_operation )
319 VikLayerParamData rv;
322 case PARAM_FILES: rv.sl = vdl->files; break;
323 case PARAM_SOURCE: rv.u = vdl->source; break;
324 case PARAM_TYPE: rv.u = vdl->type; break;
325 case PARAM_COLOR: vik_gc_get_fg_color ( vdl->gcs[0], &(rv.c) ); break;
327 /* Convert for display in desired units
328 NB file operation always in internal units (metres) */
329 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
330 rv.d = VIK_METERS_TO_FEET(vdl->min_elev);
332 rv.d = vdl->min_elev;
335 /* Convert for display in desired units
336 NB file operation always in internal units (metres) */
337 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
338 rv.d = VIK_METERS_TO_FEET(vdl->max_elev);
340 rv.d = vdl->max_elev;
346 static void dem_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file )
348 /* nothing ATM, but keep in case it's needed the future */
351 VikDEMLayer *vik_dem_layer_new ( )
353 VikDEMLayer *vdl = VIK_DEM_LAYER ( g_object_new ( VIK_DEM_LAYER_TYPE, NULL ) );
355 vik_layer_init ( VIK_LAYER(vdl), VIK_LAYER_DEM );
359 vdl->gcs = g_malloc(sizeof(GdkGC *)*DEM_N_HEIGHT_COLORS);
360 vdl->gcsgradient = g_malloc(sizeof(GdkGC *)*DEM_N_GRADIENT_COLORS);
361 /* make new gcs only if we need it (copy layer -> use old) */
364 vdl->max_elev = 1000.0;
365 vdl->source = DEM_SOURCE_SRTM;
366 vdl->type = DEM_TYPE_HEIGHT;
371 static inline guint16 get_height_difference(gint16 elev, gint16 new_elev)
373 if(new_elev == VIK_DEM_INVALID_ELEVATION)
376 return abs(new_elev - elev);
380 static void vik_dem_layer_draw_dem ( VikDEMLayer *vdl, VikViewport *vp, VikDEM *dem )
382 VikDEMColumn *column, *prevcolumn, *nextcolumn;
384 struct LatLon dem_northeast, dem_southwest;
385 gdouble max_lat, max_lon, min_lat, min_lon;
387 /**** Check if viewport and DEM data overlap ****/
389 /* get min, max lat/lon of viewport */
390 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
392 /* get min, max lat/lon of DEM data */
393 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
394 dem_northeast.lat = dem->max_north / 3600.0;
395 dem_northeast.lon = dem->max_east / 3600.0;
396 dem_southwest.lat = dem->min_north / 3600.0;
397 dem_southwest.lon = dem->min_east / 3600.0;
398 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
399 struct UTM dem_northeast_utm, dem_southwest_utm;
400 dem_northeast_utm.northing = dem->max_north;
401 dem_northeast_utm.easting = dem->max_east;
402 dem_southwest_utm.northing = dem->min_north;
403 dem_southwest_utm.easting = dem->min_east;
404 dem_northeast_utm.zone = dem_southwest_utm.zone = dem->utm_zone;
405 dem_northeast_utm.letter = dem_southwest_utm.letter = dem->utm_letter;
407 a_coords_utm_to_latlon(&dem_northeast_utm, &dem_northeast);
408 a_coords_utm_to_latlon(&dem_southwest_utm, &dem_southwest);
411 if ( (max_lat > dem_northeast.lat && min_lat > dem_northeast.lat) ||
412 (max_lat < dem_southwest.lat && min_lat < dem_southwest.lat) )
414 else if ( (max_lon > dem_northeast.lon && min_lon > dem_northeast.lon) ||
415 (max_lon < dem_southwest.lon && min_lon < dem_southwest.lon) )
417 /* else they overlap */
419 /**** End Overlap Check ****/
420 /* boxes to show where we have DEM instead of actually drawing the DEM.
421 * useful if we want to see what areas we have coverage for (if we want
422 * to get elevation data for a track) but don't want to cover the map.
426 /* draw a box if a DEM is loaded. in future I'd like to add an option for this
427 * this is useful if we want to see what areas we have dem for but don't want to
428 * cover the map (or maybe we just need translucent DEM?) */
430 VikCoord demne, demsw;
432 vik_coord_load_from_latlon(&demne, vik_viewport_get_coord_mode(vp), &dem_northeast);
433 vik_coord_load_from_latlon(&demsw, vik_viewport_get_coord_mode(vp), &dem_southwest);
435 vik_viewport_coord_to_screen ( vp, &demne, &x1, &y1 );
436 vik_viewport_coord_to_screen ( vp, &demsw, &x2, &y2 );
438 if ( x1 > vik_viewport_get_width(vp) ) x1=vik_viewport_get_width(vp);
439 if ( y2 > vik_viewport_get_height(vp) ) y2=vik_viewport_get_height(vp);
440 if ( x2 < 0 ) x2 = 0;
441 if ( y1 < 0 ) y1 = 0;
442 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
443 FALSE, x2, y1, x1-x2, y2-y1 );
448 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
449 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
451 gdouble max_lat_as, max_lon_as, min_lat_as, min_lon_as;
452 gdouble start_lat_as, end_lat_as, start_lon_as, end_lon_as;
454 gdouble start_lat, end_lat, start_lon, end_lon;
456 struct LatLon counter;
458 guint x, y, start_x, start_y;
462 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 80 ); /* todo: smarter calculation. */
464 gdouble nscale_deg = dem->north_scale / ((gdouble) 3600);
465 gdouble escale_deg = dem->east_scale / ((gdouble) 3600);
467 max_lat_as = max_lat * 3600;
468 min_lat_as = min_lat * 3600;
469 max_lon_as = max_lon * 3600;
470 min_lon_as = min_lon * 3600;
472 start_lat_as = MAX(min_lat_as, dem->min_north);
473 end_lat_as = MIN(max_lat_as, dem->max_north);
474 start_lon_as = MAX(min_lon_as, dem->min_east);
475 end_lon_as = MIN(max_lon_as, dem->max_east);
477 start_lat = floor(start_lat_as / dem->north_scale) * nscale_deg;
478 end_lat = ceil (end_lat_as / dem->north_scale) * nscale_deg;
479 start_lon = floor(start_lon_as / dem->east_scale) * escale_deg;
480 end_lon = ceil (end_lon_as / dem->east_scale) * escale_deg;
482 vik_dem_east_north_to_xy ( dem, start_lon_as, start_lat_as, &start_x, &start_y );
483 guint gradient_skip_factor = 1;
484 if(vdl->type == DEM_TYPE_GRADIENT)
485 gradient_skip_factor = skip_factor;
487 /* verify sane elev interval */
488 if ( vdl->max_elev <= vdl->min_elev )
489 vdl->max_elev = vdl->min_elev + 1;
491 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 ) {
492 // NOTE: ( counter.lon <= end_lon + ESCALE_DEG*SKIP_FACTOR ) is neccessary so in high zoom modes,
493 // the leftmost column does also get drawn, if the center point is out of viewport.
494 if ( x >= 0 && x < dem->n_columns ) {
495 column = g_ptr_array_index ( dem->columns, x );
496 // get previous and next column. catch out-of-bound.
498 new_x -= gradient_skip_factor;
500 prevcolumn = g_ptr_array_index ( dem->columns, x+1);
502 prevcolumn = g_ptr_array_index ( dem->columns, new_x);
504 new_x += gradient_skip_factor;
505 if(new_x >= dem->n_columns)
506 nextcolumn = g_ptr_array_index ( dem->columns, x-1);
508 nextcolumn = g_ptr_array_index ( dem->columns, new_x);
510 for ( y=start_y, counter.lat = start_lat; counter.lat <= end_lat; counter.lat += nscale_deg * skip_factor, y += skip_factor ) {
511 if ( y > column->n_points )
514 elev = column->points[y];
516 // calculate bounding box for drawing
517 gint box_x, box_y, box_width, box_height;
520 box_c.lat += (nscale_deg * skip_factor)/2;
521 box_c.lon -= (escale_deg * skip_factor)/2;
522 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
523 vik_viewport_coord_to_screen(vp, &tmp, &box_x, &box_y);
524 // catch box at borders
529 box_c.lat -= nscale_deg * skip_factor;
530 box_c.lon += escale_deg * skip_factor;
531 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
532 vik_viewport_coord_to_screen(vp, &tmp, &box_width, &box_height);
535 // catch box at borders
536 if(box_width < 0 || box_height < 0)
537 continue; // skip this. this is out of our viewport anyway. FIXME: why?
539 gboolean below_minimum = FALSE;
540 if(vdl->type == DEM_TYPE_HEIGHT) {
541 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev ) {
542 // Prevent 'elev - vdl->min_elev' from being negative so can safely use as array index
543 elev = ceil ( vdl->min_elev );
544 below_minimum = TRUE;
546 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
547 elev = vdl->max_elev;
551 if(box_width < 0 || box_height < 0) // FIXME: why does this happen?
554 if(vdl->type == DEM_TYPE_GRADIENT) {
555 if( elev == VIK_DEM_INVALID_ELEVATION ) {
558 // calculate and sum gradient in all directions
562 // calculate gradient from height points all around the current one
563 new_y = y - gradient_skip_factor;
566 change += get_height_difference(elev, prevcolumn->points[new_y]);
567 change += get_height_difference(elev, column->points[new_y]);
568 change += get_height_difference(elev, nextcolumn->points[new_y]);
570 change += get_height_difference(elev, prevcolumn->points[y]);
571 change += get_height_difference(elev, nextcolumn->points[y]);
573 new_y = y + gradient_skip_factor;
574 if(new_y >= column->n_points)
576 change += get_height_difference(elev, prevcolumn->points[new_y]);
577 change += get_height_difference(elev, column->points[new_y]);
578 change += get_height_difference(elev, nextcolumn->points[new_y]);
580 change = change / ((skip_factor > 1) ? log(skip_factor) : 0.55); // FIXME: better calc.
582 if(change < vdl->min_elev)
583 // Prevent 'change - vdl->min_elev' from being negative so can safely use as array index
584 change = ceil ( vdl->min_elev );
586 if(change > vdl->max_elev)
587 change = vdl->max_elev;
589 // void vik_viewport_draw_rectangle ( VikViewport *vvp, GdkGC *gc, gboolean filled, gint x1, gint y1, gint x2, gint y2 );
590 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);
593 if(vdl->type == DEM_TYPE_HEIGHT) {
594 if ( elev == VIK_DEM_INVALID_ELEVATION )
595 ; /* don't draw it */
596 else if ( elev <= 0 || below_minimum )
597 /* If 'sea' colour or below the defined mininum draw in the configurable colour */
598 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, box_x, box_y, box_width, box_height);
600 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);
607 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
608 gdouble max_nor, max_eas, min_nor, min_eas;
609 gdouble start_nor, start_eas, end_nor, end_eas;
613 guint x, y, start_x, start_y;
615 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
618 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 10 ); /* todo: smarter calculation. */
620 VikCoord tleft, tright, bleft, bright;
622 vik_viewport_screen_to_coord ( vp, 0, 0, &tleft );
623 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), 0, &tright );
624 vik_viewport_screen_to_coord ( vp, 0, vik_viewport_get_height(vp), &bleft );
625 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), vik_viewport_get_height(vp), &bright );
628 vik_coord_convert(&tleft, VIK_COORD_UTM);
629 vik_coord_convert(&tright, VIK_COORD_UTM);
630 vik_coord_convert(&bleft, VIK_COORD_UTM);
631 vik_coord_convert(&bright, VIK_COORD_UTM);
633 max_nor = MAX(tleft.north_south, tright.north_south);
634 min_nor = MIN(bleft.north_south, bright.north_south);
635 max_eas = MAX(bright.east_west, tright.east_west);
636 min_eas = MIN(bleft.east_west, tleft.east_west);
638 start_nor = MAX(min_nor, dem->min_north);
639 end_nor = MIN(max_nor, dem->max_north);
640 if ( tleft.utm_zone == dem->utm_zone && bleft.utm_zone == dem->utm_zone
641 && (tleft.utm_letter >= 'N') == (dem->utm_letter >= 'N')
642 && (bleft.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
643 start_eas = MAX(min_eas, dem->min_east);
645 start_eas = dem->min_east;
646 if ( tright.utm_zone == dem->utm_zone && bright.utm_zone == dem->utm_zone
647 && (tright.utm_letter >= 'N') == (dem->utm_letter >= 'N')
648 && (bright.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
649 end_eas = MIN(max_eas, dem->max_east);
651 end_eas = dem->max_east;
653 start_nor = floor(start_nor / dem->north_scale) * dem->north_scale;
654 end_nor = ceil (end_nor / dem->north_scale) * dem->north_scale;
655 start_eas = floor(start_eas / dem->east_scale) * dem->east_scale;
656 end_eas = ceil (end_eas / dem->east_scale) * dem->east_scale;
658 vik_dem_east_north_to_xy ( dem, start_eas, start_nor, &start_x, &start_y );
660 /* TODO: why start_x and start_y are -1 -- rounding error from above? */
662 counter.zone = dem->utm_zone;
663 counter.letter = dem->utm_letter;
665 for ( x=start_x, counter.easting = start_eas; counter.easting <= end_eas; counter.easting += dem->east_scale * skip_factor, x += skip_factor ) {
666 if ( x > 0 && x < dem->n_columns ) {
667 column = g_ptr_array_index ( dem->columns, x );
668 for ( y=start_y, counter.northing = start_nor; counter.northing <= end_nor; counter.northing += dem->north_scale * skip_factor, y += skip_factor ) {
669 if ( y > column->n_points )
671 elev = column->points[y];
672 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev )
674 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
679 vik_coord_load_from_utm(&tmp, vik_viewport_get_coord_mode(vp), &counter);
680 vik_viewport_coord_to_screen(vp, &tmp, &a, &b);
681 if ( elev == VIK_DEM_INVALID_ELEVATION )
682 ; /* don't draw it */
683 else if ( elev <= 0 )
684 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, a-1, b-1, 2, 2 );
686 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 );
694 /* return the continent for the specified lat, lon */
696 static const gchar *srtm_continent_dir ( gint lat, gint lon )
698 extern const char *_srtm_continent_data[];
699 static GHashTable *srtm_continent = NULL;
700 const gchar *continent;
703 if (!srtm_continent) {
706 srtm_continent = g_hash_table_new(g_str_hash, g_str_equal);
707 s = _srtm_continent_data;
708 while (*s != (gchar *)-1) {
711 g_hash_table_insert(srtm_continent, (gpointer) *s, (gpointer) continent);
717 g_snprintf(name, sizeof(name), "%c%02d%c%03d",
718 (lat >= 0) ? 'N' : 'S', ABS(lat),
719 (lon >= 0) ? 'E' : 'W', ABS(lon));
721 return(g_hash_table_lookup(srtm_continent, name));
724 void vik_dem_layer_draw ( VikDEMLayer *vdl, gpointer data )
726 VikViewport *vp = (VikViewport *) data;
727 GList *dems_iter = vdl->files;
731 /* search for SRTM3 90m */
733 if ( vdl->source == DEM_SOURCE_SRTM )
734 srtm_draw_existence ( vp );
735 #ifdef VIK_CONFIG_DEM24K
736 else if ( vdl->source == DEM_SOURCE_DEM24K )
737 dem24k_draw_existence ( vp );
740 while ( dems_iter ) {
741 dem = a_dems_get ( (const char *) (dems_iter->data) );
743 vik_dem_layer_draw_dem ( vdl, vp, dem );
744 dems_iter = dems_iter->next;
748 void vik_dem_layer_free ( VikDEMLayer *vdl )
751 if ( vdl->color != NULL )
752 g_object_unref ( vdl->color );
755 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
756 g_object_unref ( vdl->gcs[i] );
759 if ( vdl->gcsgradient )
760 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
761 g_object_unref ( vdl->gcsgradient[i] );
762 g_free ( vdl->gcsgradient );
764 a_dems_list_free ( vdl->files );
767 VikDEMLayer *vik_dem_layer_create ( VikViewport *vp )
769 VikDEMLayer *vdl = vik_dem_layer_new ();
772 /* TODO: share GCS between layers */
773 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
774 vdl->gcs[i] = vik_viewport_new_gc ( vp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
776 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
777 vdl->gcsgradient[i] = vik_viewport_new_gc ( vp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
781 /**************************************************************
782 **** SOURCES & DOWNLOADING
783 **************************************************************/
789 VikDEMLayer *vdl; /* NULL if not alive */
795 /**************************************************
797 **************************************************/
799 static void srtm_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
802 const gchar *continent_dir;
804 intlat = (int)floor(p->lat);
805 intlon = (int)floor(p->lon);
806 continent_dir = srtm_continent_dir(intlat, intlon);
808 if (!continent_dir) {
809 g_warning(N_("No SRTM data available for %f, %f"), p->lat, p->lon);
813 gchar *src_fn = g_strdup_printf("%s%s/%c%02d%c%03d.hgt.zip",
816 (intlat >= 0) ? 'N' : 'S',
818 (intlon >= 0) ? 'E' : 'W',
821 static DownloadOptions options = { 0, NULL, 0, a_check_map_file };
822 a_http_download_get_url ( SRTM_HTTP_SITE, src_fn, p->dest, &options, NULL );
826 static gchar *srtm_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
829 const gchar *continent_dir;
831 intlat = (int)floor(lat);
832 intlon = (int)floor(lon);
833 continent_dir = srtm_continent_dir(intlat, intlon);
836 continent_dir = "nowhere";
838 return g_strdup_printf("srtm3-%s%s%c%02d%c%03d.hgt.zip",
841 (intlat >= 0) ? 'N' : 'S',
843 (intlon >= 0) ? 'E' : 'W',
848 /* TODO: generalize */
849 static void srtm_draw_existence ( VikViewport *vp )
851 gdouble max_lat, max_lon, min_lat, min_lon;
852 gchar buf[strlen(MAPS_CACHE_DIR)+strlen(SRTM_CACHE_TEMPLATE)+30];
855 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
857 for (i = floor(min_lat); i <= floor(max_lat); i++) {
858 for (j = floor(min_lon); j <= floor(max_lon); j++) {
859 const gchar *continent_dir;
860 if ((continent_dir = srtm_continent_dir(i, j)) == NULL)
862 g_snprintf(buf, sizeof(buf), SRTM_CACHE_TEMPLATE,
866 (i >= 0) ? 'N' : 'S',
868 (j >= 0) ? 'E' : 'W',
870 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
875 sw.mode = VIK_COORD_LATLON;
876 ne.north_south = i+1;
878 ne.mode = VIK_COORD_LATLON;
879 vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
880 vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
881 if ( x1 < 0 ) x1 = 0;
882 if ( y2 < 0 ) y2 = 0;
883 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
884 FALSE, x1, y2, x2-x1, y1-y2 );
891 /**************************************************
893 **************************************************/
895 #ifdef VIK_CONFIG_DEM24K
897 static void dem24k_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
900 gchar *cmdline = g_strdup_printf("%s %.03f %.03f",
901 DEM24K_DOWNLOAD_SCRIPT,
904 /* FIX: don't use system, use execv or something. check for existence */
908 static gchar *dem24k_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
910 return g_strdup_printf("dem24k/%d/%d/%.03f,%.03f.dem",
917 /* TODO: generalize */
918 static void dem24k_draw_existence ( VikViewport *vp )
920 gdouble max_lat, max_lon, min_lat, min_lon;
921 gchar buf[strlen(MAPS_CACHE_DIR)+40];
924 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
926 for (i = floor(min_lat*8)/8; i <= floor(max_lat*8)/8; i+=0.125) {
927 /* check lat dir first -- faster */
928 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/",
931 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
933 for (j = floor(min_lon*8)/8; j <= floor(max_lon*8)/8; j+=0.125) {
934 /* check lon dir first -- faster */
935 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/",
939 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
941 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/%.03f,%.03f.dem",
947 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
951 sw.east_west = j-0.125;
952 sw.mode = VIK_COORD_LATLON;
953 ne.north_south = i+0.125;
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 /**************************************************
969 * SOURCES -- DOWNLOADING & IMPORTING TOOL *
970 **************************************************
973 static void weak_ref_cb ( gpointer ptr, GObject * dead_vdl )
975 DEMDownloadParams *p = ptr;
976 g_mutex_lock ( p->mutex );
978 g_mutex_unlock ( p->mutex );
981 /* Try to add file full_path.
982 * full_path will be copied.
983 * returns FALSE if file does not exists, TRUE otherwise.
985 static gboolean dem_layer_add_file ( VikDEMLayer *vdl, const gchar *full_path )
987 if ( g_file_test(full_path, G_FILE_TEST_EXISTS ) == TRUE ) {
988 /* only load if file size is not 0 (not in progress) */
990 stat (full_path, &sb);
992 gchar *duped_path = g_strdup(full_path);
993 vdl->files = g_list_prepend ( vdl->files, duped_path );
994 a_dems_load ( duped_path );
995 g_debug("%s: %s", __FUNCTION__, duped_path);
996 vik_layer_emit_update ( VIK_LAYER(vdl) );
1003 static void dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
1005 if ( p->source == DEM_SOURCE_SRTM )
1006 srtm_dem_download_thread ( p, threaddata );
1007 #ifdef VIK_CONFIG_DEM24K
1008 else if ( p->source == DEM_SOURCE_DEM24K )
1009 dem24k_dem_download_thread ( p, threaddata );
1012 gdk_threads_enter();
1013 g_mutex_lock ( p->mutex );
1015 g_object_weak_unref ( G_OBJECT(p->vdl), weak_ref_cb, p );
1017 if ( dem_layer_add_file ( p->vdl, p->dest ) )
1018 vik_layer_emit_update ( VIK_LAYER(p->vdl) );
1020 g_mutex_unlock ( p->mutex );
1021 gdk_threads_leave();
1025 static void free_dem_download_params ( DEMDownloadParams *p )
1027 g_mutex_free ( p->mutex );
1032 static gpointer dem_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1038 static gboolean dem_layer_download_release ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1044 gchar *dem_file = NULL;
1046 if ( vdl->source == DEM_SOURCE_NONE )
1047 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vdl), _("No download source selected. Edit layer properties.") );
1049 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1050 vik_coord_to_latlon ( &coord, &ll );
1053 if ( vdl->source == DEM_SOURCE_SRTM )
1054 dem_file = srtm_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1055 #ifdef VIK_CONFIG_DEM24K
1056 else if ( vdl->source == DEM_SOURCE_DEM24K )
1057 dem_file = dem24k_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1063 full_path = g_strdup_printf("%s%s", MAPS_CACHE_DIR, dem_file );
1065 g_debug("%s: %s", __FUNCTION__, full_path);
1067 // TODO: check if already in filelist
1069 if ( ! dem_layer_add_file(vdl, full_path) ) {
1070 gchar *tmp = g_strdup_printf ( _("Downloading DEM %s"), dem_file );
1071 DEMDownloadParams *p = g_malloc(sizeof(DEMDownloadParams));
1072 p->dest = g_strdup(full_path);
1076 p->mutex = g_mutex_new();
1077 p->source = vdl->source;
1078 g_object_weak_ref(G_OBJECT(p->vdl), weak_ref_cb, p );
1080 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vdl), tmp,
1081 (vik_thr_func) dem_download_thread, p,
1082 (vik_thr_free_func) free_dem_download_params, NULL, 1 );
1087 g_free ( dem_file );
1088 g_free ( full_path );
1093 static gboolean dem_layer_download_click ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1095 /* choose & keep track of cache dir
1096 * download in background thread
1097 * download over area */