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
100 static gchar *params_type[] = {
101 N_("Absolute height"),
102 N_("Height gradient"),
106 enum { DEM_SOURCE_SRTM,
107 #ifdef VIK_CONFIG_DEM24K
112 enum { DEM_TYPE_HEIGHT = 0,
117 static VikLayerParam dem_layer_params[] = {
118 { "files", VIK_LAYER_PARAM_STRING_LIST, VIK_LAYER_GROUP_NONE, N_("DEM Files:"), VIK_LAYER_WIDGET_FILELIST },
119 { "source", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Download Source:"), VIK_LAYER_WIDGET_RADIOGROUP_STATIC, params_source, NULL },
120 { "color", VIK_LAYER_PARAM_COLOR, VIK_LAYER_GROUP_NONE, N_("Min Elev Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
121 { "type", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Type:"), VIK_LAYER_WIDGET_RADIOGROUP_STATIC, params_type, NULL },
122 { "min_elev", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_GROUP_NONE, N_("Min Elev:"), VIK_LAYER_WIDGET_SPINBUTTON, param_scales + 0 },
123 { "max_elev", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_GROUP_NONE, N_("Max Elev:"), VIK_LAYER_WIDGET_SPINBUTTON, param_scales + 0 },
127 enum { PARAM_FILES=0, PARAM_SOURCE, PARAM_COLOR, PARAM_TYPE, PARAM_MIN_ELEV, PARAM_MAX_ELEV, NUM_PARAMS };
129 static gpointer dem_layer_download_create ( VikWindow *vw, VikViewport *vvp);
130 static gboolean dem_layer_download_release ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp );
131 static gboolean dem_layer_download_click ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp );
133 static VikToolInterface dem_tools[] = {
134 { N_("DEM Download/Import"), (VikToolConstructorFunc) dem_layer_download_create, NULL, NULL, NULL,
135 (VikToolMouseFunc) dem_layer_download_click, NULL, (VikToolMouseFunc) dem_layer_download_release,
136 (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_demdl_pixbuf },
141 The first entry is blue for a default 'sea' colour,
142 however the value used by the corresponding gc can be configured as part of the DEM layer properties.
143 The other colours, shaded from brown to white are used to give an indication of height.
145 static gchar *dem_height_colors[] = {
147 "#9b793c", "#9c7d40", "#9d8144", "#9e8549", "#9f894d", "#a08d51", "#a29156", "#a3955a", "#a4995e", "#a69d63",
148 "#a89f65", "#aaa267", "#ada569", "#afa76b", "#b1aa6d", "#b4ad6f", "#b6b071", "#b9b373", "#bcb676", "#beb978",
149 "#c0bc7a", "#c2c07d", "#c4c37f", "#c6c681", "#c8ca84", "#cacd86", "#ccd188", "#cfd58b", "#c2ce84", "#b5c87e",
150 "#a9c278", "#9cbb71", "#8fb56b", "#83af65", "#76a95e", "#6aa358", "#5e9d52", "#63a055", "#69a458", "#6fa85c",
151 "#74ac5f", "#7ab063", "#80b467", "#86b86a", "#8cbc6e", "#92c072", "#94c175", "#97c278", "#9ac47c", "#9cc57f",
152 "#9fc682", "#a2c886", "#a4c989", "#a7cb8d", "#aacd91", "#afce99", "#b5d0a1", "#bbd2aa", "#c0d3b2", "#c6d5ba",
153 "#ccd7c3", "#d1d9cb", "#d7dbd4", "#DDDDDD", "#e0e0e0", "#e4e4e4", "#e8e8e8", "#ebebeb", "#efefef", "#f3f3f3",
154 "#f7f7f7", "#fbfbfb", "#ffffff"
157 static const guint DEM_N_HEIGHT_COLORS = sizeof(dem_height_colors)/sizeof(dem_height_colors[0]);
160 "#9b793c", "#9e8549", "#a29156", "#a69d63", "#ada569", "#b4ad6f", "#bcb676", "#c2c07d", "#c8ca84", "#cfd58b",
161 "#a9c278", "#83af65", "#5e9d52", "#6fa85c", "#80b467", "#92c072", "#9ac47c", "#a2c886", "#aacd91", "#bbd2aa",
162 "#ccd7c3", "#DDDDDD", "#e8e8e8", "#f3f3f3", "#FFFFFF"
166 static gchar *dem_gradient_colors[] = {
168 "#000000", "#000011", "#000022", "#000033", "#000044", "#00004c", "#000055", "#00005d", "#000066", "#00006e",
169 "#000077", "#00007f", "#000088", "#000090", "#000099", "#0000a1", "#0000aa", "#0000b2", "#0000bb", "#0000c3",
170 "#0000cc", "#0000d4", "#0000dd", "#0000e5", "#0000ee", "#0000f6", "#0000ff", "#0008f7", "#0011ee", "#0019e6",
171 "#0022dd", "#002ad5", "#0033cc", "#003bc4", "#0044bb", "#004cb3", "#0055aa", "#005da2", "#006699", "#006e91",
172 "#007788", "#007f80", "#008877", "#00906f", "#009966", "#00a15e", "#00aa55", "#00b24d", "#00bb44", "#00c33c",
173 "#00cc33", "#00d42b", "#00dd22", "#00e51a", "#00ee11", "#00f609", "#00ff00", "#08f700", "#11ee00", "#19e600",
174 "#22dd00", "#2ad500", "#33cc00", "#3bc400", "#44bb00", "#4cb300", "#55aa00", "#5da200", "#669900", "#6e9100",
175 "#778800", "#7f8000", "#887700", "#906f00", "#996600", "#a15e00", "#aa5500", "#b24d00", "#bb4400", "#c33c00",
176 "#cc3300", "#d42b00", "#dd2200", "#e51a00", "#ee1100", "#f60900", "#ff0000",
180 static const guint DEM_N_GRADIENT_COLORS = sizeof(dem_gradient_colors)/sizeof(dem_gradient_colors[0]);
183 VikLayerInterface vik_dem_layer_interface = {
188 sizeof(dem_tools) / sizeof(dem_tools[0]),
197 (VikLayerFuncCreate) vik_dem_layer_create,
198 (VikLayerFuncRealize) NULL,
199 (VikLayerFuncPostRead) dem_layer_post_read,
200 (VikLayerFuncFree) vik_dem_layer_free,
202 (VikLayerFuncProperties) NULL,
203 (VikLayerFuncDraw) vik_dem_layer_draw,
204 (VikLayerFuncChangeCoordMode) NULL,
206 (VikLayerFuncSetMenuItemsSelection) NULL,
207 (VikLayerFuncGetMenuItemsSelection) NULL,
209 (VikLayerFuncAddMenuItems) NULL,
210 (VikLayerFuncSublayerAddMenuItems) NULL,
212 (VikLayerFuncSublayerRenameRequest) NULL,
213 (VikLayerFuncSublayerToggleVisible) NULL,
214 (VikLayerFuncSublayerTooltip) NULL,
215 (VikLayerFuncLayerTooltip) dem_layer_tooltip,
216 (VikLayerFuncLayerSelected) NULL,
218 (VikLayerFuncMarshall) dem_layer_marshall,
219 (VikLayerFuncUnmarshall) dem_layer_unmarshall,
221 (VikLayerFuncSetParam) dem_layer_set_param,
222 (VikLayerFuncGetParam) dem_layer_get_param,
224 (VikLayerFuncReadFileData) NULL,
225 (VikLayerFuncWriteFileData) NULL,
227 (VikLayerFuncDeleteItem) NULL,
228 (VikLayerFuncCutItem) NULL,
229 (VikLayerFuncCopyItem) NULL,
230 (VikLayerFuncPasteItem) NULL,
231 (VikLayerFuncFreeCopiedItem) NULL,
232 (VikLayerFuncDragDropRequest) NULL,
234 (VikLayerFuncSelectClick) NULL,
235 (VikLayerFuncSelectMove) NULL,
236 (VikLayerFuncSelectRelease) NULL,
237 (VikLayerFuncSelectedViewportMenu) NULL,
240 struct _VikDEMLayer {
252 GType vik_dem_layer_get_type ()
254 static GType vdl_type = 0;
258 static const GTypeInfo vdl_info =
260 sizeof (VikDEMLayerClass),
261 NULL, /* base_init */
262 NULL, /* base_finalize */
263 NULL, /* class init */
264 NULL, /* class_finalize */
265 NULL, /* class_data */
266 sizeof (VikDEMLayer),
268 NULL /* instance init */
270 vdl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikDEMLayer", &vdl_info, 0 );
276 static const gchar* dem_layer_tooltip( VikDEMLayer *vdl )
278 static gchar tmp_buf[100];
279 g_snprintf (tmp_buf, sizeof(tmp_buf), _("Number of files: %d"), g_list_length (vdl->files));
283 static void dem_layer_marshall( VikDEMLayer *vdl, guint8 **data, gint *len )
285 vik_layer_marshall_params ( VIK_LAYER(vdl), data, len );
288 static VikDEMLayer *dem_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
290 VikDEMLayer *rv = vik_dem_layer_new ();
293 /* TODO: share GCS between layers */
294 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
295 rv->gcs[i] = vik_viewport_new_gc ( vvp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
297 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
298 rv->gcsgradient[i] = vik_viewport_new_gc ( vvp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
300 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
304 /* Structure for DEM data used in background thread */
307 } dem_load_thread_data;
310 * Function for starting the DEM file loading as a background thread
312 static int dem_layer_load_list_thread ( dem_load_thread_data *dltd, gpointer threaddata )
314 int result = 0; // Default to good
316 if ( a_dems_load_list ( &(dltd->vdl->files), threaddata ) ) {
321 // ATM as each file is processed the screen is not updated (no mechanism exposed to a_dems_load_list)
322 // Thus force draw only at the end, as loading is complete/aborted
324 // Test is helpful to prevent Gtk-CRITICAL warnings if the program is exitted whilst loading
325 if ( IS_VIK_LAYER(dltd->vdl) )
326 vik_layer_emit_update ( VIK_LAYER(dltd->vdl) );
332 static void dem_layer_thread_data_free ( dem_load_thread_data *data )
338 static void dem_layer_thread_cancel ( dem_load_thread_data *data )
341 // Instead of freeing the list, leave it as partially processed
342 // Thus we can see/use what was done
345 gboolean dem_layer_set_param ( VikDEMLayer *vdl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
349 case PARAM_COLOR: gdk_gc_set_rgb_fg_color ( vdl->gcs[0], &(data.c) ); break;
350 case PARAM_SOURCE: vdl->source = data.u; break;
351 case PARAM_TYPE: vdl->type = data.u; break;
353 /* Convert to store internally
354 NB file operation always in internal units (metres) */
355 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
356 vdl->min_elev = VIK_FEET_TO_METERS(data.d);
358 vdl->min_elev = data.d;
361 /* Convert to store internally
362 NB file operation always in internal units (metres) */
363 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
364 vdl->max_elev = VIK_FEET_TO_METERS(data.d);
366 vdl->max_elev = data.d;
370 // Clear out old settings - if any commonalities with new settings they will have to be read again
371 a_dems_list_free ( vdl->files );
372 // Set file list so any other intermediate screen drawing updates will show currently loaded DEMs by the working thread
373 vdl->files = data.sl;
375 dem_load_thread_data *dltd = g_malloc ( sizeof(dem_load_thread_data) );
377 dltd->vdl->files = data.sl;
379 a_background_thread ( VIK_GTK_WINDOW_FROM_WIDGET(vp),
381 (vik_thr_func) dem_layer_load_list_thread,
383 (vik_thr_free_func) dem_layer_thread_data_free,
384 (vik_thr_free_func) dem_layer_thread_cancel,
385 g_list_length ( data.sl ) ); // Number of DEM files
392 static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gboolean is_file_operation )
394 VikLayerParamData rv;
397 case PARAM_FILES: rv.sl = vdl->files; break;
398 case PARAM_SOURCE: rv.u = vdl->source; break;
399 case PARAM_TYPE: rv.u = vdl->type; break;
400 case PARAM_COLOR: vik_gc_get_fg_color ( vdl->gcs[0], &(rv.c) ); break;
402 /* Convert for display in desired units
403 NB file operation always in internal units (metres) */
404 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
405 rv.d = VIK_METERS_TO_FEET(vdl->min_elev);
407 rv.d = vdl->min_elev;
410 /* Convert for display in desired units
411 NB file operation always in internal units (metres) */
412 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
413 rv.d = VIK_METERS_TO_FEET(vdl->max_elev);
415 rv.d = vdl->max_elev;
421 static void dem_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file )
423 /* nothing ATM, but keep in case it's needed the future */
426 VikDEMLayer *vik_dem_layer_new ( )
428 VikDEMLayer *vdl = VIK_DEM_LAYER ( g_object_new ( VIK_DEM_LAYER_TYPE, NULL ) );
430 vik_layer_init ( VIK_LAYER(vdl), VIK_LAYER_DEM );
434 vdl->gcs = g_malloc(sizeof(GdkGC *)*DEM_N_HEIGHT_COLORS);
435 vdl->gcsgradient = g_malloc(sizeof(GdkGC *)*DEM_N_GRADIENT_COLORS);
436 /* make new gcs only if we need it (copy layer -> use old) */
439 vdl->max_elev = 1000.0;
440 vdl->source = DEM_SOURCE_SRTM;
441 vdl->type = DEM_TYPE_HEIGHT;
446 static inline guint16 get_height_difference(gint16 elev, gint16 new_elev)
448 if(new_elev == VIK_DEM_INVALID_ELEVATION)
451 return abs(new_elev - elev);
455 static void vik_dem_layer_draw_dem ( VikDEMLayer *vdl, VikViewport *vp, VikDEM *dem )
457 VikDEMColumn *column, *prevcolumn, *nextcolumn;
459 struct LatLon dem_northeast, dem_southwest;
460 gdouble max_lat, max_lon, min_lat, min_lon;
462 /**** Check if viewport and DEM data overlap ****/
464 /* get min, max lat/lon of viewport */
465 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
467 /* get min, max lat/lon of DEM data */
468 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
469 dem_northeast.lat = dem->max_north / 3600.0;
470 dem_northeast.lon = dem->max_east / 3600.0;
471 dem_southwest.lat = dem->min_north / 3600.0;
472 dem_southwest.lon = dem->min_east / 3600.0;
473 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
474 struct UTM dem_northeast_utm, dem_southwest_utm;
475 dem_northeast_utm.northing = dem->max_north;
476 dem_northeast_utm.easting = dem->max_east;
477 dem_southwest_utm.northing = dem->min_north;
478 dem_southwest_utm.easting = dem->min_east;
479 dem_northeast_utm.zone = dem_southwest_utm.zone = dem->utm_zone;
480 dem_northeast_utm.letter = dem_southwest_utm.letter = dem->utm_letter;
482 a_coords_utm_to_latlon(&dem_northeast_utm, &dem_northeast);
483 a_coords_utm_to_latlon(&dem_southwest_utm, &dem_southwest);
486 if ( (max_lat > dem_northeast.lat && min_lat > dem_northeast.lat) ||
487 (max_lat < dem_southwest.lat && min_lat < dem_southwest.lat) )
489 else if ( (max_lon > dem_northeast.lon && min_lon > dem_northeast.lon) ||
490 (max_lon < dem_southwest.lon && min_lon < dem_southwest.lon) )
492 /* else they overlap */
494 /**** End Overlap Check ****/
495 /* boxes to show where we have DEM instead of actually drawing the DEM.
496 * useful if we want to see what areas we have coverage for (if we want
497 * to get elevation data for a track) but don't want to cover the map.
501 /* draw a box if a DEM is loaded. in future I'd like to add an option for this
502 * this is useful if we want to see what areas we have dem for but don't want to
503 * cover the map (or maybe we just need translucent DEM?) */
505 VikCoord demne, demsw;
507 vik_coord_load_from_latlon(&demne, vik_viewport_get_coord_mode(vp), &dem_northeast);
508 vik_coord_load_from_latlon(&demsw, vik_viewport_get_coord_mode(vp), &dem_southwest);
510 vik_viewport_coord_to_screen ( vp, &demne, &x1, &y1 );
511 vik_viewport_coord_to_screen ( vp, &demsw, &x2, &y2 );
513 if ( x1 > vik_viewport_get_width(vp) ) x1=vik_viewport_get_width(vp);
514 if ( y2 > vik_viewport_get_height(vp) ) y2=vik_viewport_get_height(vp);
515 if ( x2 < 0 ) x2 = 0;
516 if ( y1 < 0 ) y1 = 0;
517 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
518 FALSE, x2, y1, x1-x2, y2-y1 );
523 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
524 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
526 gdouble max_lat_as, max_lon_as, min_lat_as, min_lon_as;
527 gdouble start_lat_as, end_lat_as, start_lon_as, end_lon_as;
529 gdouble start_lat, end_lat, start_lon, end_lon;
531 struct LatLon counter;
533 guint x, y, start_x, start_y;
537 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 80 ); /* todo: smarter calculation. */
539 gdouble nscale_deg = dem->north_scale / ((gdouble) 3600);
540 gdouble escale_deg = dem->east_scale / ((gdouble) 3600);
542 max_lat_as = max_lat * 3600;
543 min_lat_as = min_lat * 3600;
544 max_lon_as = max_lon * 3600;
545 min_lon_as = min_lon * 3600;
547 start_lat_as = MAX(min_lat_as, dem->min_north);
548 end_lat_as = MIN(max_lat_as, dem->max_north);
549 start_lon_as = MAX(min_lon_as, dem->min_east);
550 end_lon_as = MIN(max_lon_as, dem->max_east);
552 start_lat = floor(start_lat_as / dem->north_scale) * nscale_deg;
553 end_lat = ceil (end_lat_as / dem->north_scale) * nscale_deg;
554 start_lon = floor(start_lon_as / dem->east_scale) * escale_deg;
555 end_lon = ceil (end_lon_as / dem->east_scale) * escale_deg;
557 vik_dem_east_north_to_xy ( dem, start_lon_as, start_lat_as, &start_x, &start_y );
558 guint gradient_skip_factor = 1;
559 if(vdl->type == DEM_TYPE_GRADIENT)
560 gradient_skip_factor = skip_factor;
562 /* verify sane elev interval */
563 if ( vdl->max_elev <= vdl->min_elev )
564 vdl->max_elev = vdl->min_elev + 1;
566 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 ) {
567 // NOTE: ( counter.lon <= end_lon + ESCALE_DEG*SKIP_FACTOR ) is neccessary so in high zoom modes,
568 // the leftmost column does also get drawn, if the center point is out of viewport.
569 if ( x >= 0 && x < dem->n_columns ) {
570 column = g_ptr_array_index ( dem->columns, x );
571 // get previous and next column. catch out-of-bound.
573 new_x -= gradient_skip_factor;
575 prevcolumn = g_ptr_array_index ( dem->columns, x+1);
577 prevcolumn = g_ptr_array_index ( dem->columns, new_x);
579 new_x += gradient_skip_factor;
580 if(new_x >= dem->n_columns)
581 nextcolumn = g_ptr_array_index ( dem->columns, x-1);
583 nextcolumn = g_ptr_array_index ( dem->columns, new_x);
585 for ( y=start_y, counter.lat = start_lat; counter.lat <= end_lat; counter.lat += nscale_deg * skip_factor, y += skip_factor ) {
586 if ( y > column->n_points )
589 elev = column->points[y];
591 // calculate bounding box for drawing
592 gint box_x, box_y, box_width, box_height;
595 box_c.lat += (nscale_deg * skip_factor)/2;
596 box_c.lon -= (escale_deg * skip_factor)/2;
597 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
598 vik_viewport_coord_to_screen(vp, &tmp, &box_x, &box_y);
599 // catch box at borders
604 box_c.lat -= nscale_deg * skip_factor;
605 box_c.lon += escale_deg * skip_factor;
606 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
607 vik_viewport_coord_to_screen(vp, &tmp, &box_width, &box_height);
610 // catch box at borders
611 if(box_width < 0 || box_height < 0)
612 continue; // skip this. this is out of our viewport anyway. FIXME: why?
614 gboolean below_minimum = FALSE;
615 if(vdl->type == DEM_TYPE_HEIGHT) {
616 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev ) {
617 // Prevent 'elev - vdl->min_elev' from being negative so can safely use as array index
618 elev = ceil ( vdl->min_elev );
619 below_minimum = TRUE;
621 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
622 elev = vdl->max_elev;
626 if(box_width < 0 || box_height < 0) // FIXME: why does this happen?
629 if(vdl->type == DEM_TYPE_GRADIENT) {
630 if( elev == VIK_DEM_INVALID_ELEVATION ) {
633 // calculate and sum gradient in all directions
637 // calculate gradient from height points all around the current one
638 new_y = y - gradient_skip_factor;
641 change += get_height_difference(elev, prevcolumn->points[new_y]);
642 change += get_height_difference(elev, column->points[new_y]);
643 change += get_height_difference(elev, nextcolumn->points[new_y]);
645 change += get_height_difference(elev, prevcolumn->points[y]);
646 change += get_height_difference(elev, nextcolumn->points[y]);
648 new_y = y + gradient_skip_factor;
649 if(new_y >= column->n_points)
651 change += get_height_difference(elev, prevcolumn->points[new_y]);
652 change += get_height_difference(elev, column->points[new_y]);
653 change += get_height_difference(elev, nextcolumn->points[new_y]);
655 change = change / ((skip_factor > 1) ? log(skip_factor) : 0.55); // FIXME: better calc.
657 if(change < vdl->min_elev)
658 // Prevent 'change - vdl->min_elev' from being negative so can safely use as array index
659 change = ceil ( vdl->min_elev );
661 if(change > vdl->max_elev)
662 change = vdl->max_elev;
664 // void vik_viewport_draw_rectangle ( VikViewport *vvp, GdkGC *gc, gboolean filled, gint x1, gint y1, gint x2, gint y2 );
665 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);
668 if(vdl->type == DEM_TYPE_HEIGHT) {
669 if ( elev == VIK_DEM_INVALID_ELEVATION )
670 ; /* don't draw it */
671 else if ( elev <= 0 || below_minimum )
672 /* If 'sea' colour or below the defined mininum draw in the configurable colour */
673 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, box_x, box_y, box_width, box_height);
675 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);
682 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
683 gdouble max_nor, max_eas, min_nor, min_eas;
684 gdouble start_nor, start_eas, end_nor, end_eas;
688 guint x, y, start_x, start_y;
690 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
693 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 10 ); /* todo: smarter calculation. */
695 VikCoord tleft, tright, bleft, bright;
697 vik_viewport_screen_to_coord ( vp, 0, 0, &tleft );
698 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), 0, &tright );
699 vik_viewport_screen_to_coord ( vp, 0, vik_viewport_get_height(vp), &bleft );
700 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), vik_viewport_get_height(vp), &bright );
703 vik_coord_convert(&tleft, VIK_COORD_UTM);
704 vik_coord_convert(&tright, VIK_COORD_UTM);
705 vik_coord_convert(&bleft, VIK_COORD_UTM);
706 vik_coord_convert(&bright, VIK_COORD_UTM);
708 max_nor = MAX(tleft.north_south, tright.north_south);
709 min_nor = MIN(bleft.north_south, bright.north_south);
710 max_eas = MAX(bright.east_west, tright.east_west);
711 min_eas = MIN(bleft.east_west, tleft.east_west);
713 start_nor = MAX(min_nor, dem->min_north);
714 end_nor = MIN(max_nor, dem->max_north);
715 if ( tleft.utm_zone == dem->utm_zone && bleft.utm_zone == dem->utm_zone
716 && (tleft.utm_letter >= 'N') == (dem->utm_letter >= 'N')
717 && (bleft.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
718 start_eas = MAX(min_eas, dem->min_east);
720 start_eas = dem->min_east;
721 if ( tright.utm_zone == dem->utm_zone && bright.utm_zone == dem->utm_zone
722 && (tright.utm_letter >= 'N') == (dem->utm_letter >= 'N')
723 && (bright.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
724 end_eas = MIN(max_eas, dem->max_east);
726 end_eas = dem->max_east;
728 start_nor = floor(start_nor / dem->north_scale) * dem->north_scale;
729 end_nor = ceil (end_nor / dem->north_scale) * dem->north_scale;
730 start_eas = floor(start_eas / dem->east_scale) * dem->east_scale;
731 end_eas = ceil (end_eas / dem->east_scale) * dem->east_scale;
733 vik_dem_east_north_to_xy ( dem, start_eas, start_nor, &start_x, &start_y );
735 /* TODO: why start_x and start_y are -1 -- rounding error from above? */
737 counter.zone = dem->utm_zone;
738 counter.letter = dem->utm_letter;
740 for ( x=start_x, counter.easting = start_eas; counter.easting <= end_eas; counter.easting += dem->east_scale * skip_factor, x += skip_factor ) {
741 if ( x > 0 && x < dem->n_columns ) {
742 column = g_ptr_array_index ( dem->columns, x );
743 for ( y=start_y, counter.northing = start_nor; counter.northing <= end_nor; counter.northing += dem->north_scale * skip_factor, y += skip_factor ) {
744 if ( y > column->n_points )
746 elev = column->points[y];
747 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev )
749 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
754 vik_coord_load_from_utm(&tmp, vik_viewport_get_coord_mode(vp), &counter);
755 vik_viewport_coord_to_screen(vp, &tmp, &a, &b);
756 if ( elev == VIK_DEM_INVALID_ELEVATION )
757 ; /* don't draw it */
758 else if ( elev <= 0 )
759 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, a-1, b-1, 2, 2 );
761 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 );
769 /* return the continent for the specified lat, lon */
771 static const gchar *srtm_continent_dir ( gint lat, gint lon )
773 extern const char *_srtm_continent_data[];
774 static GHashTable *srtm_continent = NULL;
775 const gchar *continent;
778 if (!srtm_continent) {
781 srtm_continent = g_hash_table_new(g_str_hash, g_str_equal);
782 s = _srtm_continent_data;
783 while (*s != (gchar *)-1) {
786 g_hash_table_insert(srtm_continent, (gpointer) *s, (gpointer) continent);
792 g_snprintf(name, sizeof(name), "%c%02d%c%03d",
793 (lat >= 0) ? 'N' : 'S', ABS(lat),
794 (lon >= 0) ? 'E' : 'W', ABS(lon));
796 return(g_hash_table_lookup(srtm_continent, name));
799 void vik_dem_layer_draw ( VikDEMLayer *vdl, gpointer data )
801 VikViewport *vp = (VikViewport *) data;
802 GList *dems_iter = vdl->files;
806 /* search for SRTM3 90m */
808 if ( vdl->source == DEM_SOURCE_SRTM )
809 srtm_draw_existence ( vp );
810 #ifdef VIK_CONFIG_DEM24K
811 else if ( vdl->source == DEM_SOURCE_DEM24K )
812 dem24k_draw_existence ( vp );
815 while ( dems_iter ) {
816 dem = a_dems_get ( (const char *) (dems_iter->data) );
818 vik_dem_layer_draw_dem ( vdl, vp, dem );
819 dems_iter = dems_iter->next;
823 void vik_dem_layer_free ( VikDEMLayer *vdl )
826 if ( vdl->color != NULL )
827 g_object_unref ( vdl->color );
830 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
831 g_object_unref ( vdl->gcs[i] );
834 if ( vdl->gcsgradient )
835 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
836 g_object_unref ( vdl->gcsgradient[i] );
837 g_free ( vdl->gcsgradient );
839 a_dems_list_free ( vdl->files );
842 VikDEMLayer *vik_dem_layer_create ( VikViewport *vp )
844 VikDEMLayer *vdl = vik_dem_layer_new ();
847 /* TODO: share GCS between layers */
848 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
849 vdl->gcs[i] = vik_viewport_new_gc ( vp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
851 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
852 vdl->gcsgradient[i] = vik_viewport_new_gc ( vp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
856 /**************************************************************
857 **** SOURCES & DOWNLOADING
858 **************************************************************/
864 VikDEMLayer *vdl; /* NULL if not alive */
870 /**************************************************
872 **************************************************/
874 static void srtm_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
877 const gchar *continent_dir;
879 intlat = (int)floor(p->lat);
880 intlon = (int)floor(p->lon);
881 continent_dir = srtm_continent_dir(intlat, intlon);
883 if (!continent_dir) {
884 g_warning(N_("No SRTM data available for %f, %f"), p->lat, p->lon);
888 gchar *src_fn = g_strdup_printf("%s%s/%c%02d%c%03d.hgt.zip",
891 (intlat >= 0) ? 'N' : 'S',
893 (intlon >= 0) ? 'E' : 'W',
896 static DownloadMapOptions options = { FALSE, FALSE, NULL, 0, a_check_map_file };
897 a_http_download_get_url ( SRTM_HTTP_SITE, src_fn, p->dest, &options, NULL );
901 static gchar *srtm_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
904 const gchar *continent_dir;
906 intlat = (int)floor(lat);
907 intlon = (int)floor(lon);
908 continent_dir = srtm_continent_dir(intlat, intlon);
911 continent_dir = "nowhere";
913 return g_strdup_printf("srtm3-%s%s%c%02d%c%03d.hgt.zip",
916 (intlat >= 0) ? 'N' : 'S',
918 (intlon >= 0) ? 'E' : 'W',
923 /* TODO: generalize */
924 static void srtm_draw_existence ( VikViewport *vp )
926 gdouble max_lat, max_lon, min_lat, min_lon;
927 gchar buf[strlen(MAPS_CACHE_DIR)+strlen(SRTM_CACHE_TEMPLATE)+30];
930 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
932 for (i = floor(min_lat); i <= floor(max_lat); i++) {
933 for (j = floor(min_lon); j <= floor(max_lon); j++) {
934 const gchar *continent_dir;
935 if ((continent_dir = srtm_continent_dir(i, j)) == NULL)
937 g_snprintf(buf, sizeof(buf), SRTM_CACHE_TEMPLATE,
941 (i >= 0) ? 'N' : 'S',
943 (j >= 0) ? 'E' : 'W',
945 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
950 sw.mode = VIK_COORD_LATLON;
951 ne.north_south = i+1;
953 ne.mode = VIK_COORD_LATLON;
954 vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
955 vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
956 if ( x1 < 0 ) x1 = 0;
957 if ( y2 < 0 ) y2 = 0;
958 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
959 FALSE, x1, y2, x2-x1, y1-y2 );
966 /**************************************************
968 **************************************************/
970 #ifdef VIK_CONFIG_DEM24K
972 static void dem24k_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
975 gchar *cmdline = g_strdup_printf("%s %.03f %.03f",
976 DEM24K_DOWNLOAD_SCRIPT,
979 /* FIX: don't use system, use execv or something. check for existence */
983 static gchar *dem24k_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
985 return g_strdup_printf("dem24k/%d/%d/%.03f,%.03f.dem",
992 /* TODO: generalize */
993 static void dem24k_draw_existence ( VikViewport *vp )
995 gdouble max_lat, max_lon, min_lat, min_lon;
996 gchar buf[strlen(MAPS_CACHE_DIR)+40];
999 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
1001 for (i = floor(min_lat*8)/8; i <= floor(max_lat*8)/8; i+=0.125) {
1002 /* check lat dir first -- faster */
1003 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/",
1006 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1008 for (j = floor(min_lon*8)/8; j <= floor(max_lon*8)/8; j+=0.125) {
1009 /* check lon dir first -- faster */
1010 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/",
1014 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1016 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/%.03f,%.03f.dem",
1022 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1024 gint x1, y1, x2, y2;
1026 sw.east_west = j-0.125;
1027 sw.mode = VIK_COORD_LATLON;
1028 ne.north_south = i+0.125;
1030 ne.mode = VIK_COORD_LATLON;
1031 vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
1032 vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
1033 if ( x1 < 0 ) x1 = 0;
1034 if ( y2 < 0 ) y2 = 0;
1035 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
1036 FALSE, x1, y2, x2-x1, y1-y2 );
1043 /**************************************************
1044 * SOURCES -- DOWNLOADING & IMPORTING TOOL *
1045 **************************************************
1048 static void weak_ref_cb ( gpointer ptr, GObject * dead_vdl )
1050 DEMDownloadParams *p = ptr;
1051 g_mutex_lock ( p->mutex );
1053 g_mutex_unlock ( p->mutex );
1056 /* Try to add file full_path.
1057 * full_path will be copied.
1058 * returns FALSE if file does not exists, TRUE otherwise.
1060 static gboolean dem_layer_add_file ( VikDEMLayer *vdl, const gchar *full_path )
1062 if ( g_file_test(full_path, G_FILE_TEST_EXISTS ) == TRUE ) {
1063 /* only load if file size is not 0 (not in progress) */
1065 stat (full_path, &sb);
1067 gchar *duped_path = g_strdup(full_path);
1068 vdl->files = g_list_prepend ( vdl->files, duped_path );
1069 a_dems_load ( duped_path );
1070 g_debug("%s: %s", __FUNCTION__, duped_path);
1071 vik_layer_emit_update ( VIK_LAYER(vdl) );
1078 static void dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
1080 if ( p->source == DEM_SOURCE_SRTM )
1081 srtm_dem_download_thread ( p, threaddata );
1082 #ifdef VIK_CONFIG_DEM24K
1083 else if ( p->source == DEM_SOURCE_DEM24K )
1084 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 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1124 vik_coord_to_latlon ( &coord, &ll );
1127 if ( vdl->source == DEM_SOURCE_SRTM )
1128 dem_file = srtm_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1129 #ifdef VIK_CONFIG_DEM24K
1130 else if ( vdl->source == DEM_SOURCE_DEM24K )
1131 dem_file = dem24k_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1137 full_path = g_strdup_printf("%s%s", MAPS_CACHE_DIR, dem_file );
1139 g_debug("%s: %s", __FUNCTION__, full_path);
1141 // TODO: check if already in filelist
1143 if ( ! dem_layer_add_file(vdl, full_path) ) {
1144 gchar *tmp = g_strdup_printf ( _("Downloading DEM %s"), dem_file );
1145 DEMDownloadParams *p = g_malloc(sizeof(DEMDownloadParams));
1146 p->dest = g_strdup(full_path);
1150 p->mutex = g_mutex_new();
1151 p->source = vdl->source;
1152 g_object_weak_ref(G_OBJECT(p->vdl), weak_ref_cb, p );
1154 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vdl), tmp,
1155 (vik_thr_func) dem_download_thread, p,
1156 (vik_thr_free_func) free_dem_download_params, NULL, 1 );
1161 g_free ( dem_file );
1162 g_free ( full_path );
1167 static gboolean dem_layer_download_click ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1169 /* choose & keep track of cache dir
1170 * download in background thread
1171 * download over area */