2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #ifdef HAVE_SYS_TYPES_H
29 #include <sys/types.h>
31 #ifdef HAVE_SYS_STAT_H
38 #include <glib/gi18n.h>
45 #include "background.h"
46 #include "vikwaypoint.h"
48 #include "vikviewport.h"
49 #include "viktreeview.h"
51 #include "vikaggregatelayer.h"
52 #include "viklayerspanel.h"
53 #include "vikmapslayer.h"
54 #include "vikdemlayer.h"
60 #include "icons/icons.h"
62 #define MAPS_CACHE_DIR maps_layer_default_dir()
64 #define SRTM_CACHE_TEMPLATE "%ssrtm3-%s%s%c%02d%c%03d.hgt.zip"
65 #define SRTM_HTTP_SITE "dds.cr.usgs.gov"
66 #define SRTM_HTTP_URI "/srtm/version2_1/SRTM3/"
68 #ifdef VIK_CONFIG_DEM24K
69 #define DEM24K_DOWNLOAD_SCRIPT "dem24k.pl"
72 #define UNUSED_LINE_THICKNESS 3
74 static const gchar* dem_layer_tooltip( VikDEMLayer *vdl );
75 static void dem_layer_marshall( VikDEMLayer *vdl, guint8 **data, gint *len );
76 static VikDEMLayer *dem_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
77 static gboolean dem_layer_set_param ( VikDEMLayer *vdl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
78 static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gboolean is_file_operation );
79 static void dem_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file );
80 static void srtm_draw_existence ( VikViewport *vp );
82 #ifdef VIK_CONFIG_DEM24K
83 static void dem24k_draw_existence ( VikViewport *vp );
86 /* Upped upper limit incase units are feet */
87 static VikLayerParamScale param_scales[] = {
92 static gchar *params_source[] = {
93 "SRTM Global 90m (3 arcsec)",
94 #ifdef VIK_CONFIG_DEM24K
101 static gchar *params_type[] = {
102 N_("Absolute height"),
103 N_("Height gradient"),
107 enum { DEM_SOURCE_SRTM,
108 #ifdef VIK_CONFIG_DEM24K
114 enum { DEM_TYPE_HEIGHT = 0,
119 static VikLayerParam dem_layer_params[] = {
120 { "files", VIK_LAYER_PARAM_STRING_LIST, VIK_LAYER_GROUP_NONE, N_("DEM Files:"), VIK_LAYER_WIDGET_FILELIST },
121 { "source", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Download Source:"), VIK_LAYER_WIDGET_RADIOGROUP_STATIC, params_source, NULL },
122 { "color", VIK_LAYER_PARAM_COLOR, VIK_LAYER_GROUP_NONE, N_("Min Elev Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
123 { "type", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Type:"), VIK_LAYER_WIDGET_RADIOGROUP_STATIC, params_type, NULL },
124 { "min_elev", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_GROUP_NONE, N_("Min Elev:"), VIK_LAYER_WIDGET_SPINBUTTON, param_scales + 0 },
125 { "max_elev", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_GROUP_NONE, N_("Max Elev:"), VIK_LAYER_WIDGET_SPINBUTTON, param_scales + 0 },
129 enum { PARAM_FILES=0, PARAM_SOURCE, PARAM_COLOR, PARAM_TYPE, PARAM_MIN_ELEV, PARAM_MAX_ELEV, NUM_PARAMS };
131 static gpointer dem_layer_download_create ( VikWindow *vw, VikViewport *vvp);
132 static gboolean dem_layer_download_release ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp );
133 static gboolean dem_layer_download_click ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp );
135 static VikToolInterface dem_tools[] = {
136 { N_("DEM Download/Import"), (VikToolConstructorFunc) dem_layer_download_create, NULL, NULL, NULL,
137 (VikToolMouseFunc) dem_layer_download_click, NULL, (VikToolMouseFunc) dem_layer_download_release,
138 (VikToolKeyFunc) NULL, GDK_CURSOR_IS_PIXMAP, &cursor_demdl_pixbuf },
143 The first entry is blue for a default 'sea' colour,
144 however the value used by the corresponding gc can be configured as part of the DEM layer properties.
145 The other colours, shaded from brown to white are used to give an indication of height.
147 static gchar *dem_height_colors[] = {
149 "#9b793c", "#9c7d40", "#9d8144", "#9e8549", "#9f894d", "#a08d51", "#a29156", "#a3955a", "#a4995e", "#a69d63",
150 "#a89f65", "#aaa267", "#ada569", "#afa76b", "#b1aa6d", "#b4ad6f", "#b6b071", "#b9b373", "#bcb676", "#beb978",
151 "#c0bc7a", "#c2c07d", "#c4c37f", "#c6c681", "#c8ca84", "#cacd86", "#ccd188", "#cfd58b", "#c2ce84", "#b5c87e",
152 "#a9c278", "#9cbb71", "#8fb56b", "#83af65", "#76a95e", "#6aa358", "#5e9d52", "#63a055", "#69a458", "#6fa85c",
153 "#74ac5f", "#7ab063", "#80b467", "#86b86a", "#8cbc6e", "#92c072", "#94c175", "#97c278", "#9ac47c", "#9cc57f",
154 "#9fc682", "#a2c886", "#a4c989", "#a7cb8d", "#aacd91", "#afce99", "#b5d0a1", "#bbd2aa", "#c0d3b2", "#c6d5ba",
155 "#ccd7c3", "#d1d9cb", "#d7dbd4", "#DDDDDD", "#e0e0e0", "#e4e4e4", "#e8e8e8", "#ebebeb", "#efefef", "#f3f3f3",
156 "#f7f7f7", "#fbfbfb", "#ffffff"
159 static const guint DEM_N_HEIGHT_COLORS = sizeof(dem_height_colors)/sizeof(dem_height_colors[0]);
162 "#9b793c", "#9e8549", "#a29156", "#a69d63", "#ada569", "#b4ad6f", "#bcb676", "#c2c07d", "#c8ca84", "#cfd58b",
163 "#a9c278", "#83af65", "#5e9d52", "#6fa85c", "#80b467", "#92c072", "#9ac47c", "#a2c886", "#aacd91", "#bbd2aa",
164 "#ccd7c3", "#DDDDDD", "#e8e8e8", "#f3f3f3", "#FFFFFF"
168 static gchar *dem_gradient_colors[] = {
170 "#000000", "#000011", "#000022", "#000033", "#000044", "#00004c", "#000055", "#00005d", "#000066", "#00006e",
171 "#000077", "#00007f", "#000088", "#000090", "#000099", "#0000a1", "#0000aa", "#0000b2", "#0000bb", "#0000c3",
172 "#0000cc", "#0000d4", "#0000dd", "#0000e5", "#0000ee", "#0000f6", "#0000ff", "#0008f7", "#0011ee", "#0019e6",
173 "#0022dd", "#002ad5", "#0033cc", "#003bc4", "#0044bb", "#004cb3", "#0055aa", "#005da2", "#006699", "#006e91",
174 "#007788", "#007f80", "#008877", "#00906f", "#009966", "#00a15e", "#00aa55", "#00b24d", "#00bb44", "#00c33c",
175 "#00cc33", "#00d42b", "#00dd22", "#00e51a", "#00ee11", "#00f609", "#00ff00", "#08f700", "#11ee00", "#19e600",
176 "#22dd00", "#2ad500", "#33cc00", "#3bc400", "#44bb00", "#4cb300", "#55aa00", "#5da200", "#669900", "#6e9100",
177 "#778800", "#7f8000", "#887700", "#906f00", "#996600", "#a15e00", "#aa5500", "#b24d00", "#bb4400", "#c33c00",
178 "#cc3300", "#d42b00", "#dd2200", "#e51a00", "#ee1100", "#f60900", "#ff0000",
182 static const guint DEM_N_GRADIENT_COLORS = sizeof(dem_gradient_colors)/sizeof(dem_gradient_colors[0]);
185 VikLayerInterface vik_dem_layer_interface = {
190 sizeof(dem_tools) / sizeof(dem_tools[0]),
199 (VikLayerFuncCreate) vik_dem_layer_create,
200 (VikLayerFuncRealize) NULL,
201 (VikLayerFuncPostRead) dem_layer_post_read,
202 (VikLayerFuncFree) vik_dem_layer_free,
204 (VikLayerFuncProperties) NULL,
205 (VikLayerFuncDraw) vik_dem_layer_draw,
206 (VikLayerFuncChangeCoordMode) NULL,
208 (VikLayerFuncSetMenuItemsSelection) NULL,
209 (VikLayerFuncGetMenuItemsSelection) NULL,
211 (VikLayerFuncAddMenuItems) NULL,
212 (VikLayerFuncSublayerAddMenuItems) NULL,
214 (VikLayerFuncSublayerRenameRequest) NULL,
215 (VikLayerFuncSublayerToggleVisible) NULL,
216 (VikLayerFuncSublayerTooltip) NULL,
217 (VikLayerFuncLayerTooltip) dem_layer_tooltip,
218 (VikLayerFuncLayerSelected) NULL,
220 (VikLayerFuncMarshall) dem_layer_marshall,
221 (VikLayerFuncUnmarshall) dem_layer_unmarshall,
223 (VikLayerFuncSetParam) dem_layer_set_param,
224 (VikLayerFuncGetParam) dem_layer_get_param,
226 (VikLayerFuncReadFileData) NULL,
227 (VikLayerFuncWriteFileData) NULL,
229 (VikLayerFuncDeleteItem) NULL,
230 (VikLayerFuncCutItem) NULL,
231 (VikLayerFuncCopyItem) NULL,
232 (VikLayerFuncPasteItem) NULL,
233 (VikLayerFuncFreeCopiedItem) NULL,
234 (VikLayerFuncDragDropRequest) NULL,
236 (VikLayerFuncSelectClick) NULL,
239 struct _VikDEMLayer {
251 GType vik_dem_layer_get_type ()
253 static GType vdl_type = 0;
257 static const GTypeInfo vdl_info =
259 sizeof (VikDEMLayerClass),
260 NULL, /* base_init */
261 NULL, /* base_finalize */
262 NULL, /* class init */
263 NULL, /* class_finalize */
264 NULL, /* class_data */
265 sizeof (VikDEMLayer),
267 NULL /* instance init */
269 vdl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikDEMLayer", &vdl_info, 0 );
275 static const gchar* dem_layer_tooltip( VikDEMLayer *vdl )
277 static gchar tmp_buf[100];
278 g_snprintf (tmp_buf, sizeof(tmp_buf), _("Number of files: %d"), g_list_length (vdl->files));
282 static void dem_layer_marshall( VikDEMLayer *vdl, guint8 **data, gint *len )
284 vik_layer_marshall_params ( VIK_LAYER(vdl), data, len );
287 static VikDEMLayer *dem_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
289 VikDEMLayer *rv = vik_dem_layer_new ();
292 /* TODO: share GCS between layers */
293 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
294 rv->gcs[i] = vik_viewport_new_gc ( vvp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
296 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
297 rv->gcsgradient[i] = vik_viewport_new_gc ( vvp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
299 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
303 /* Structure for DEM data used in background thread */
306 } dem_load_thread_data;
309 * Function for starting the DEM file loading as a background thread
311 static int dem_layer_load_list_thread ( dem_load_thread_data *dltd, gpointer threaddata )
313 int result = 0; // Default to good
315 if ( a_dems_load_list ( &(dltd->vdl->files), threaddata ) ) {
320 // ATM as each file is processed the screen is not updated (no mechanism exposed to a_dems_load_list)
321 // Thus force draw only at the end, as loading is complete/aborted
323 // Test is helpful to prevent Gtk-CRITICAL warnings if the program is exitted whilst loading
324 if ( IS_VIK_LAYER(dltd->vdl) )
325 vik_layer_emit_update ( VIK_LAYER(dltd->vdl) );
331 static void dem_layer_thread_data_free ( dem_load_thread_data *data )
337 static void dem_layer_thread_cancel ( dem_load_thread_data *data )
340 // Instead of freeing the list, leave it as partially processed
341 // Thus we can see/use what was done
344 gboolean dem_layer_set_param ( VikDEMLayer *vdl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
348 case PARAM_COLOR: gdk_gc_set_rgb_fg_color ( vdl->gcs[0], &(data.c) ); break;
349 case PARAM_SOURCE: vdl->source = data.u; break;
350 case PARAM_TYPE: vdl->type = data.u; break;
352 /* Convert to store internally
353 NB file operation always in internal units (metres) */
354 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
355 vdl->min_elev = VIK_FEET_TO_METERS(data.d);
357 vdl->min_elev = data.d;
360 /* Convert to store internally
361 NB file operation always in internal units (metres) */
362 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
363 vdl->max_elev = VIK_FEET_TO_METERS(data.d);
365 vdl->max_elev = data.d;
369 // Clear out old settings - if any commonalities with new settings they will have to be read again
370 a_dems_list_free ( vdl->files );
371 // Set file list so any other intermediate screen drawing updates will show currently loaded DEMs by the working thread
372 vdl->files = data.sl;
374 dem_load_thread_data *dltd = g_malloc ( sizeof(dem_load_thread_data) );
376 dltd->vdl->files = data.sl;
378 a_background_thread ( VIK_GTK_WINDOW_FROM_WIDGET(vp),
380 (vik_thr_func) dem_layer_load_list_thread,
382 (vik_thr_free_func) dem_layer_thread_data_free,
383 (vik_thr_free_func) dem_layer_thread_cancel,
384 g_list_length ( data.sl ) ); // Number of DEM files
391 static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gboolean is_file_operation )
393 VikLayerParamData rv;
396 case PARAM_FILES: rv.sl = vdl->files; break;
397 case PARAM_SOURCE: rv.u = vdl->source; break;
398 case PARAM_TYPE: rv.u = vdl->type; break;
399 case PARAM_COLOR: vik_gc_get_fg_color ( vdl->gcs[0], &(rv.c) ); break;
401 /* Convert for display in desired units
402 NB file operation always in internal units (metres) */
403 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
404 rv.d = VIK_METERS_TO_FEET(vdl->min_elev);
406 rv.d = vdl->min_elev;
409 /* Convert for display in desired units
410 NB file operation always in internal units (metres) */
411 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
412 rv.d = VIK_METERS_TO_FEET(vdl->max_elev);
414 rv.d = vdl->max_elev;
420 static void dem_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file )
422 /* nothing ATM, but keep in case it's needed the future */
425 VikDEMLayer *vik_dem_layer_new ( )
427 VikDEMLayer *vdl = VIK_DEM_LAYER ( g_object_new ( VIK_DEM_LAYER_TYPE, NULL ) );
429 vik_layer_init ( VIK_LAYER(vdl), VIK_LAYER_DEM );
433 vdl->gcs = g_malloc(sizeof(GdkGC *)*DEM_N_HEIGHT_COLORS);
434 vdl->gcsgradient = g_malloc(sizeof(GdkGC *)*DEM_N_GRADIENT_COLORS);
435 /* make new gcs only if we need it (copy layer -> use old) */
438 vdl->max_elev = 1000.0;
439 vdl->source = DEM_SOURCE_SRTM;
440 vdl->type = DEM_TYPE_HEIGHT;
445 static inline guint16 get_height_difference(gint16 elev, gint16 new_elev)
447 if(new_elev == VIK_DEM_INVALID_ELEVATION)
450 return abs(new_elev - elev);
454 static void vik_dem_layer_draw_dem ( VikDEMLayer *vdl, VikViewport *vp, VikDEM *dem )
456 VikDEMColumn *column, *prevcolumn, *nextcolumn;
458 struct LatLon dem_northeast, dem_southwest;
459 gdouble max_lat, max_lon, min_lat, min_lon;
461 /**** Check if viewport and DEM data overlap ****/
463 /* get min, max lat/lon of viewport */
464 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
466 /* get min, max lat/lon of DEM data */
467 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
468 dem_northeast.lat = dem->max_north / 3600.0;
469 dem_northeast.lon = dem->max_east / 3600.0;
470 dem_southwest.lat = dem->min_north / 3600.0;
471 dem_southwest.lon = dem->min_east / 3600.0;
472 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
473 struct UTM dem_northeast_utm, dem_southwest_utm;
474 dem_northeast_utm.northing = dem->max_north;
475 dem_northeast_utm.easting = dem->max_east;
476 dem_southwest_utm.northing = dem->min_north;
477 dem_southwest_utm.easting = dem->min_east;
478 dem_northeast_utm.zone = dem_southwest_utm.zone = dem->utm_zone;
479 dem_northeast_utm.letter = dem_southwest_utm.letter = dem->utm_letter;
481 a_coords_utm_to_latlon(&dem_northeast_utm, &dem_northeast);
482 a_coords_utm_to_latlon(&dem_southwest_utm, &dem_southwest);
485 if ( (max_lat > dem_northeast.lat && min_lat > dem_northeast.lat) ||
486 (max_lat < dem_southwest.lat && min_lat < dem_southwest.lat) )
488 else if ( (max_lon > dem_northeast.lon && min_lon > dem_northeast.lon) ||
489 (max_lon < dem_southwest.lon && min_lon < dem_southwest.lon) )
491 /* else they overlap */
493 /**** End Overlap Check ****/
494 /* boxes to show where we have DEM instead of actually drawing the DEM.
495 * useful if we want to see what areas we have coverage for (if we want
496 * to get elevation data for a track) but don't want to cover the map.
500 /* draw a box if a DEM is loaded. in future I'd like to add an option for this
501 * this is useful if we want to see what areas we have dem for but don't want to
502 * cover the map (or maybe we just need translucent DEM?) */
504 VikCoord demne, demsw;
506 vik_coord_load_from_latlon(&demne, vik_viewport_get_coord_mode(vp), &dem_northeast);
507 vik_coord_load_from_latlon(&demsw, vik_viewport_get_coord_mode(vp), &dem_southwest);
509 vik_viewport_coord_to_screen ( vp, &demne, &x1, &y1 );
510 vik_viewport_coord_to_screen ( vp, &demsw, &x2, &y2 );
512 if ( x1 > vik_viewport_get_width(vp) ) x1=vik_viewport_get_width(vp);
513 if ( y2 > vik_viewport_get_height(vp) ) y2=vik_viewport_get_height(vp);
514 if ( x2 < 0 ) x2 = 0;
515 if ( y1 < 0 ) y1 = 0;
516 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
517 FALSE, x2, y1, x1-x2, y2-y1 );
522 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
523 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
525 gdouble max_lat_as, max_lon_as, min_lat_as, min_lon_as;
526 gdouble start_lat_as, end_lat_as, start_lon_as, end_lon_as;
528 gdouble start_lat, end_lat, start_lon, end_lon;
530 struct LatLon counter;
532 guint x, y, start_x, start_y;
536 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 80 ); /* todo: smarter calculation. */
538 gdouble nscale_deg = dem->north_scale / ((gdouble) 3600);
539 gdouble escale_deg = dem->east_scale / ((gdouble) 3600);
541 max_lat_as = max_lat * 3600;
542 min_lat_as = min_lat * 3600;
543 max_lon_as = max_lon * 3600;
544 min_lon_as = min_lon * 3600;
546 start_lat_as = MAX(min_lat_as, dem->min_north);
547 end_lat_as = MIN(max_lat_as, dem->max_north);
548 start_lon_as = MAX(min_lon_as, dem->min_east);
549 end_lon_as = MIN(max_lon_as, dem->max_east);
551 start_lat = floor(start_lat_as / dem->north_scale) * nscale_deg;
552 end_lat = ceil (end_lat_as / dem->north_scale) * nscale_deg;
553 start_lon = floor(start_lon_as / dem->east_scale) * escale_deg;
554 end_lon = ceil (end_lon_as / dem->east_scale) * escale_deg;
556 vik_dem_east_north_to_xy ( dem, start_lon_as, start_lat_as, &start_x, &start_y );
557 guint gradient_skip_factor = 1;
558 if(vdl->type == DEM_TYPE_GRADIENT)
559 gradient_skip_factor = skip_factor;
561 /* verify sane elev interval */
562 if ( vdl->max_elev <= vdl->min_elev )
563 vdl->max_elev = vdl->min_elev + 1;
565 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 ) {
566 // NOTE: ( counter.lon <= end_lon + ESCALE_DEG*SKIP_FACTOR ) is neccessary so in high zoom modes,
567 // the leftmost column does also get drawn, if the center point is out of viewport.
568 if ( x >= 0 && x < dem->n_columns ) {
569 column = g_ptr_array_index ( dem->columns, x );
570 // get previous and next column. catch out-of-bound.
572 new_x -= gradient_skip_factor;
574 prevcolumn = g_ptr_array_index ( dem->columns, x+1);
576 prevcolumn = g_ptr_array_index ( dem->columns, new_x);
578 new_x += gradient_skip_factor;
579 if(new_x >= dem->n_columns)
580 nextcolumn = g_ptr_array_index ( dem->columns, x-1);
582 nextcolumn = g_ptr_array_index ( dem->columns, new_x);
584 for ( y=start_y, counter.lat = start_lat; counter.lat <= end_lat; counter.lat += nscale_deg * skip_factor, y += skip_factor ) {
585 if ( y > column->n_points )
588 elev = column->points[y];
590 // calculate bounding box for drawing
591 gint box_x, box_y, box_width, box_height;
594 box_c.lat += (nscale_deg * skip_factor)/2;
595 box_c.lon -= (escale_deg * skip_factor)/2;
596 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
597 vik_viewport_coord_to_screen(vp, &tmp, &box_x, &box_y);
598 // catch box at borders
603 box_c.lat -= nscale_deg * skip_factor;
604 box_c.lon += escale_deg * skip_factor;
605 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
606 vik_viewport_coord_to_screen(vp, &tmp, &box_width, &box_height);
609 // catch box at borders
610 if(box_width < 0 || box_height < 0)
611 continue; // skip this. this is out of our viewport anyway. FIXME: why?
613 gboolean below_minimum = FALSE;
614 if(vdl->type == DEM_TYPE_HEIGHT) {
615 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev ) {
616 // Prevent 'elev - vdl->min_elev' from being negative so can safely use as array index
617 elev = ceil ( vdl->min_elev );
618 below_minimum = TRUE;
620 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
621 elev = vdl->max_elev;
625 if(box_width < 0 || box_height < 0) // FIXME: why does this happen?
628 if(vdl->type == DEM_TYPE_GRADIENT) {
629 if( elev == VIK_DEM_INVALID_ELEVATION ) {
632 // calculate and sum gradient in all directions
636 // calculate gradient from height points all around the current one
637 new_y = y - gradient_skip_factor;
640 change += get_height_difference(elev, prevcolumn->points[new_y]);
641 change += get_height_difference(elev, column->points[new_y]);
642 change += get_height_difference(elev, nextcolumn->points[new_y]);
644 change += get_height_difference(elev, prevcolumn->points[y]);
645 change += get_height_difference(elev, nextcolumn->points[y]);
647 new_y = y + gradient_skip_factor;
648 if(new_y >= column->n_points)
650 change += get_height_difference(elev, prevcolumn->points[new_y]);
651 change += get_height_difference(elev, column->points[new_y]);
652 change += get_height_difference(elev, nextcolumn->points[new_y]);
654 change = change / ((skip_factor > 1) ? log(skip_factor) : 0.55); // FIXME: better calc.
656 if(change < vdl->min_elev)
657 // Prevent 'change - vdl->min_elev' from being negative so can safely use as array index
658 change = ceil ( vdl->min_elev );
660 if(change > vdl->max_elev)
661 change = vdl->max_elev;
663 // void vik_viewport_draw_rectangle ( VikViewport *vvp, GdkGC *gc, gboolean filled, gint x1, gint y1, gint x2, gint y2 );
664 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);
667 if(vdl->type == DEM_TYPE_HEIGHT) {
668 if ( elev == VIK_DEM_INVALID_ELEVATION )
669 ; /* don't draw it */
670 else if ( elev <= 0 || below_minimum )
671 /* If 'sea' colour or below the defined mininum draw in the configurable colour */
672 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, box_x, box_y, box_width, box_height);
674 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);
681 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
682 gdouble max_nor, max_eas, min_nor, min_eas;
683 gdouble start_nor, start_eas, end_nor, end_eas;
687 guint x, y, start_x, start_y;
689 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
692 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 10 ); /* todo: smarter calculation. */
694 VikCoord tleft, tright, bleft, bright;
696 vik_viewport_screen_to_coord ( vp, 0, 0, &tleft );
697 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), 0, &tright );
698 vik_viewport_screen_to_coord ( vp, 0, vik_viewport_get_height(vp), &bleft );
699 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), vik_viewport_get_height(vp), &bright );
702 vik_coord_convert(&tleft, VIK_COORD_UTM);
703 vik_coord_convert(&tright, VIK_COORD_UTM);
704 vik_coord_convert(&bleft, VIK_COORD_UTM);
705 vik_coord_convert(&bright, VIK_COORD_UTM);
707 max_nor = MAX(tleft.north_south, tright.north_south);
708 min_nor = MIN(bleft.north_south, bright.north_south);
709 max_eas = MAX(bright.east_west, tright.east_west);
710 min_eas = MIN(bleft.east_west, tleft.east_west);
712 start_nor = MAX(min_nor, dem->min_north);
713 end_nor = MIN(max_nor, dem->max_north);
714 if ( tleft.utm_zone == dem->utm_zone && bleft.utm_zone == dem->utm_zone
715 && (tleft.utm_letter >= 'N') == (dem->utm_letter >= 'N')
716 && (bleft.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
717 start_eas = MAX(min_eas, dem->min_east);
719 start_eas = dem->min_east;
720 if ( tright.utm_zone == dem->utm_zone && bright.utm_zone == dem->utm_zone
721 && (tright.utm_letter >= 'N') == (dem->utm_letter >= 'N')
722 && (bright.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
723 end_eas = MIN(max_eas, dem->max_east);
725 end_eas = dem->max_east;
727 start_nor = floor(start_nor / dem->north_scale) * dem->north_scale;
728 end_nor = ceil (end_nor / dem->north_scale) * dem->north_scale;
729 start_eas = floor(start_eas / dem->east_scale) * dem->east_scale;
730 end_eas = ceil (end_eas / dem->east_scale) * dem->east_scale;
732 vik_dem_east_north_to_xy ( dem, start_eas, start_nor, &start_x, &start_y );
734 /* TODO: why start_x and start_y are -1 -- rounding error from above? */
736 counter.zone = dem->utm_zone;
737 counter.letter = dem->utm_letter;
739 for ( x=start_x, counter.easting = start_eas; counter.easting <= end_eas; counter.easting += dem->east_scale * skip_factor, x += skip_factor ) {
740 if ( x > 0 && x < dem->n_columns ) {
741 column = g_ptr_array_index ( dem->columns, x );
742 for ( y=start_y, counter.northing = start_nor; counter.northing <= end_nor; counter.northing += dem->north_scale * skip_factor, y += skip_factor ) {
743 if ( y > column->n_points )
745 elev = column->points[y];
746 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev )
748 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
753 vik_coord_load_from_utm(&tmp, vik_viewport_get_coord_mode(vp), &counter);
754 vik_viewport_coord_to_screen(vp, &tmp, &a, &b);
755 if ( elev == VIK_DEM_INVALID_ELEVATION )
756 ; /* don't draw it */
757 else if ( elev <= 0 )
758 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, a-1, b-1, 2, 2 );
760 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 );
768 /* return the continent for the specified lat, lon */
770 static const gchar *srtm_continent_dir ( gint lat, gint lon )
772 extern const char *_srtm_continent_data[];
773 static GHashTable *srtm_continent = NULL;
774 const gchar *continent;
777 if (!srtm_continent) {
780 srtm_continent = g_hash_table_new(g_str_hash, g_str_equal);
781 s = _srtm_continent_data;
782 while (*s != (gchar *)-1) {
785 g_hash_table_insert(srtm_continent, (gpointer) *s, (gpointer) continent);
791 g_snprintf(name, sizeof(name), "%c%02d%c%03d",
792 (lat >= 0) ? 'N' : 'S', ABS(lat),
793 (lon >= 0) ? 'E' : 'W', ABS(lon));
795 return(g_hash_table_lookup(srtm_continent, name));
798 void vik_dem_layer_draw ( VikDEMLayer *vdl, gpointer data )
800 VikViewport *vp = (VikViewport *) data;
801 GList *dems_iter = vdl->files;
805 /* search for SRTM3 90m */
807 if ( vdl->source == DEM_SOURCE_SRTM )
808 srtm_draw_existence ( vp );
809 #ifdef VIK_CONFIG_DEM24K
810 else if ( vdl->source == DEM_SOURCE_DEM24K )
811 dem24k_draw_existence ( vp );
814 while ( dems_iter ) {
815 dem = a_dems_get ( (const char *) (dems_iter->data) );
817 vik_dem_layer_draw_dem ( vdl, vp, dem );
818 dems_iter = dems_iter->next;
822 void vik_dem_layer_free ( VikDEMLayer *vdl )
825 if ( vdl->color != NULL )
826 g_object_unref ( vdl->color );
829 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
830 g_object_unref ( vdl->gcs[i] );
833 if ( vdl->gcsgradient )
834 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
835 g_object_unref ( vdl->gcsgradient[i] );
836 g_free ( vdl->gcsgradient );
838 a_dems_list_free ( vdl->files );
841 VikDEMLayer *vik_dem_layer_create ( VikViewport *vp )
843 VikDEMLayer *vdl = vik_dem_layer_new ();
846 /* TODO: share GCS between layers */
847 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
848 vdl->gcs[i] = vik_viewport_new_gc ( vp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
850 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
851 vdl->gcsgradient[i] = vik_viewport_new_gc ( vp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
855 /**************************************************************
856 **** SOURCES & DOWNLOADING
857 **************************************************************/
863 VikDEMLayer *vdl; /* NULL if not alive */
869 /**************************************************
871 **************************************************/
873 static void srtm_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
876 const gchar *continent_dir;
878 intlat = (int)floor(p->lat);
879 intlon = (int)floor(p->lon);
880 continent_dir = srtm_continent_dir(intlat, intlon);
882 if (!continent_dir) {
883 g_warning(N_("No SRTM data available for %f, %f"), p->lat, p->lon);
887 gchar *src_fn = g_strdup_printf("%s%s/%c%02d%c%03d.hgt.zip",
890 (intlat >= 0) ? 'N' : 'S',
892 (intlon >= 0) ? 'E' : 'W',
895 static DownloadMapOptions options = { FALSE, FALSE, NULL, 0, a_check_map_file };
896 a_http_download_get_url ( SRTM_HTTP_SITE, src_fn, p->dest, &options, NULL );
900 static gchar *srtm_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
903 const gchar *continent_dir;
905 intlat = (int)floor(lat);
906 intlon = (int)floor(lon);
907 continent_dir = srtm_continent_dir(intlat, intlon);
910 continent_dir = "nowhere";
912 return g_strdup_printf("srtm3-%s%s%c%02d%c%03d.hgt.zip",
915 (intlat >= 0) ? 'N' : 'S',
917 (intlon >= 0) ? 'E' : 'W',
922 /* TODO: generalize */
923 static void srtm_draw_existence ( VikViewport *vp )
925 gdouble max_lat, max_lon, min_lat, min_lon;
926 gchar buf[strlen(MAPS_CACHE_DIR)+strlen(SRTM_CACHE_TEMPLATE)+30];
929 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
931 for (i = floor(min_lat); i <= floor(max_lat); i++) {
932 for (j = floor(min_lon); j <= floor(max_lon); j++) {
933 const gchar *continent_dir;
934 if ((continent_dir = srtm_continent_dir(i, j)) == NULL)
936 g_snprintf(buf, sizeof(buf), SRTM_CACHE_TEMPLATE,
940 (i >= 0) ? 'N' : 'S',
942 (j >= 0) ? 'E' : 'W',
944 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
949 sw.mode = VIK_COORD_LATLON;
950 ne.north_south = i+1;
952 ne.mode = VIK_COORD_LATLON;
953 vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
954 vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
955 if ( x1 < 0 ) x1 = 0;
956 if ( y2 < 0 ) y2 = 0;
957 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
958 FALSE, x1, y2, x2-x1, y1-y2 );
965 /**************************************************
967 **************************************************/
969 #ifdef VIK_CONFIG_DEM24K
971 static void dem24k_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
974 gchar *cmdline = g_strdup_printf("%s %.03f %.03f",
975 DEM24K_DOWNLOAD_SCRIPT,
978 /* FIX: don't use system, use execv or something. check for existence */
982 static gchar *dem24k_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
984 return g_strdup_printf("dem24k/%d/%d/%.03f,%.03f.dem",
991 /* TODO: generalize */
992 static void dem24k_draw_existence ( VikViewport *vp )
994 gdouble max_lat, max_lon, min_lat, min_lon;
995 gchar buf[strlen(MAPS_CACHE_DIR)+40];
998 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
1000 for (i = floor(min_lat*8)/8; i <= floor(max_lat*8)/8; i+=0.125) {
1001 /* check lat dir first -- faster */
1002 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/",
1005 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1007 for (j = floor(min_lon*8)/8; j <= floor(max_lon*8)/8; j+=0.125) {
1008 /* check lon dir first -- faster */
1009 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/",
1013 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1015 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/%.03f,%.03f.dem",
1021 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1023 gint x1, y1, x2, y2;
1025 sw.east_west = j-0.125;
1026 sw.mode = VIK_COORD_LATLON;
1027 ne.north_south = i+0.125;
1029 ne.mode = VIK_COORD_LATLON;
1030 vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
1031 vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
1032 if ( x1 < 0 ) x1 = 0;
1033 if ( y2 < 0 ) y2 = 0;
1034 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
1035 FALSE, x1, y2, x2-x1, y1-y2 );
1042 /**************************************************
1043 * SOURCES -- DOWNLOADING & IMPORTING TOOL *
1044 **************************************************
1047 static void weak_ref_cb ( gpointer ptr, GObject * dead_vdl )
1049 DEMDownloadParams *p = ptr;
1050 g_mutex_lock ( p->mutex );
1052 g_mutex_unlock ( p->mutex );
1055 /* Try to add file full_path.
1056 * full_path will be copied.
1057 * returns FALSE if file does not exists, TRUE otherwise.
1059 static gboolean dem_layer_add_file ( VikDEMLayer *vdl, const gchar *full_path )
1061 if ( g_file_test(full_path, G_FILE_TEST_EXISTS ) == TRUE ) {
1062 /* only load if file size is not 0 (not in progress) */
1064 stat (full_path, &sb);
1066 gchar *duped_path = g_strdup(full_path);
1067 vdl->files = g_list_prepend ( vdl->files, duped_path );
1068 a_dems_load ( duped_path );
1069 g_debug("%s: %s", __FUNCTION__, duped_path);
1070 vik_layer_emit_update ( VIK_LAYER(vdl) );
1077 static void dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
1079 if ( p->source == DEM_SOURCE_SRTM )
1080 srtm_dem_download_thread ( p, threaddata );
1081 #ifdef VIK_CONFIG_DEM24K
1082 else if ( p->source == DEM_SOURCE_DEM24K )
1083 dem24k_dem_download_thread ( p, threaddata );
1086 gdk_threads_enter();
1087 g_mutex_lock ( p->mutex );
1089 g_object_weak_unref ( G_OBJECT(p->vdl), weak_ref_cb, p );
1091 if ( dem_layer_add_file ( p->vdl, p->dest ) )
1092 vik_layer_emit_update ( VIK_LAYER(p->vdl) );
1094 g_mutex_unlock ( p->mutex );
1095 gdk_threads_leave();
1099 static void free_dem_download_params ( DEMDownloadParams *p )
1101 g_mutex_free ( p->mutex );
1106 static gpointer dem_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1112 static gboolean dem_layer_download_release ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1118 gchar *dem_file = NULL;
1120 if ( vdl->source == DEM_SOURCE_NONE )
1121 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vdl), _("No download source selected. Edit layer properties.") );
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 */