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,
219 (VikLayerFuncMarshall) dem_layer_marshall,
220 (VikLayerFuncUnmarshall) dem_layer_unmarshall,
222 (VikLayerFuncSetParam) dem_layer_set_param,
223 (VikLayerFuncGetParam) dem_layer_get_param,
225 (VikLayerFuncReadFileData) NULL,
226 (VikLayerFuncWriteFileData) NULL,
228 (VikLayerFuncDeleteItem) NULL,
229 (VikLayerFuncCutItem) NULL,
230 (VikLayerFuncCopyItem) NULL,
231 (VikLayerFuncPasteItem) NULL,
232 (VikLayerFuncFreeCopiedItem) NULL,
233 (VikLayerFuncDragDropRequest) NULL,
236 struct _VikDEMLayer {
248 GType vik_dem_layer_get_type ()
250 static GType vdl_type = 0;
254 static const GTypeInfo vdl_info =
256 sizeof (VikDEMLayerClass),
257 NULL, /* base_init */
258 NULL, /* base_finalize */
259 NULL, /* class init */
260 NULL, /* class_finalize */
261 NULL, /* class_data */
262 sizeof (VikDEMLayer),
264 NULL /* instance init */
266 vdl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikDEMLayer", &vdl_info, 0 );
272 static const gchar* dem_layer_tooltip( VikDEMLayer *vdl )
274 static gchar tmp_buf[100];
275 g_snprintf (tmp_buf, sizeof(tmp_buf), _("Number of files: %d"), g_list_length (vdl->files));
279 static void dem_layer_marshall( VikDEMLayer *vdl, guint8 **data, gint *len )
281 vik_layer_marshall_params ( VIK_LAYER(vdl), data, len );
284 static VikDEMLayer *dem_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
286 VikDEMLayer *rv = vik_dem_layer_new ();
289 /* TODO: share GCS between layers */
290 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
291 rv->gcs[i] = vik_viewport_new_gc ( vvp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
293 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
294 rv->gcsgradient[i] = vik_viewport_new_gc ( vvp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
296 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
300 /* Structure for DEM data used in background thread */
303 } dem_load_thread_data;
306 * Function for starting the DEM file loading as a background thread
308 static int dem_layer_load_list_thread ( dem_load_thread_data *dltd, gpointer threaddata )
310 int result = 0; // Default to good
312 if ( a_dems_load_list ( &(dltd->vdl->files), threaddata ) ) {
317 // ATM as each file is processed the screen is not updated (no mechanism exposed to a_dems_load_list)
318 // Thus force draw only at the end, as loading is complete/aborted
320 // Test is helpful to prevent Gtk-CRITICAL warnings if the program is exitted whilst loading
321 if ( IS_VIK_LAYER(dltd->vdl) )
322 vik_layer_emit_update ( VIK_LAYER(dltd->vdl) );
328 static void dem_layer_thread_data_free ( dem_load_thread_data *data )
334 static void dem_layer_thread_cancel ( dem_load_thread_data *data )
337 // Instead of freeing the list, leave it as partially processed
338 // Thus we can see/use what was done
341 gboolean dem_layer_set_param ( VikDEMLayer *vdl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
345 case PARAM_COLOR: gdk_gc_set_rgb_fg_color ( vdl->gcs[0], &(data.c) ); break;
346 case PARAM_SOURCE: vdl->source = data.u; break;
347 case PARAM_TYPE: vdl->type = data.u; break;
349 /* Convert to store internally
350 NB file operation always in internal units (metres) */
351 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
352 vdl->min_elev = VIK_FEET_TO_METERS(data.d);
354 vdl->min_elev = data.d;
357 /* Convert to store internally
358 NB file operation always in internal units (metres) */
359 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
360 vdl->max_elev = VIK_FEET_TO_METERS(data.d);
362 vdl->max_elev = data.d;
366 // Clear out old settings - if any commonalities with new settings they will have to be read again
367 a_dems_list_free ( vdl->files );
368 // Set file list so any other intermediate screen drawing updates will show currently loaded DEMs by the working thread
369 vdl->files = data.sl;
371 dem_load_thread_data *dltd = g_malloc ( sizeof(dem_load_thread_data) );
373 dltd->vdl->files = data.sl;
375 a_background_thread ( VIK_GTK_WINDOW_FROM_WIDGET(vp),
377 (vik_thr_func) dem_layer_load_list_thread,
379 (vik_thr_free_func) dem_layer_thread_data_free,
380 (vik_thr_free_func) dem_layer_thread_cancel,
381 g_list_length ( data.sl ) ); // Number of DEM files
388 static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gboolean is_file_operation )
390 VikLayerParamData rv;
393 case PARAM_FILES: rv.sl = vdl->files; break;
394 case PARAM_SOURCE: rv.u = vdl->source; break;
395 case PARAM_TYPE: rv.u = vdl->type; break;
396 case PARAM_COLOR: vik_gc_get_fg_color ( vdl->gcs[0], &(rv.c) ); break;
398 /* Convert for display in desired units
399 NB file operation always in internal units (metres) */
400 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
401 rv.d = VIK_METERS_TO_FEET(vdl->min_elev);
403 rv.d = vdl->min_elev;
406 /* Convert for display in desired units
407 NB file operation always in internal units (metres) */
408 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
409 rv.d = VIK_METERS_TO_FEET(vdl->max_elev);
411 rv.d = vdl->max_elev;
417 static void dem_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file )
419 /* nothing ATM, but keep in case it's needed the future */
422 VikDEMLayer *vik_dem_layer_new ( )
424 VikDEMLayer *vdl = VIK_DEM_LAYER ( g_object_new ( VIK_DEM_LAYER_TYPE, NULL ) );
426 vik_layer_init ( VIK_LAYER(vdl), VIK_LAYER_DEM );
430 vdl->gcs = g_malloc(sizeof(GdkGC *)*DEM_N_HEIGHT_COLORS);
431 vdl->gcsgradient = g_malloc(sizeof(GdkGC *)*DEM_N_GRADIENT_COLORS);
432 /* make new gcs only if we need it (copy layer -> use old) */
435 vdl->max_elev = 1000.0;
436 vdl->source = DEM_SOURCE_SRTM;
437 vdl->type = DEM_TYPE_HEIGHT;
442 static inline guint16 get_height_difference(gint16 elev, gint16 new_elev)
444 if(new_elev == VIK_DEM_INVALID_ELEVATION)
447 return abs(new_elev - elev);
451 static void vik_dem_layer_draw_dem ( VikDEMLayer *vdl, VikViewport *vp, VikDEM *dem )
453 VikDEMColumn *column, *prevcolumn, *nextcolumn;
455 struct LatLon dem_northeast, dem_southwest;
456 gdouble max_lat, max_lon, min_lat, min_lon;
458 /**** Check if viewport and DEM data overlap ****/
460 /* get min, max lat/lon of viewport */
461 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
463 /* get min, max lat/lon of DEM data */
464 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
465 dem_northeast.lat = dem->max_north / 3600.0;
466 dem_northeast.lon = dem->max_east / 3600.0;
467 dem_southwest.lat = dem->min_north / 3600.0;
468 dem_southwest.lon = dem->min_east / 3600.0;
469 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
470 struct UTM dem_northeast_utm, dem_southwest_utm;
471 dem_northeast_utm.northing = dem->max_north;
472 dem_northeast_utm.easting = dem->max_east;
473 dem_southwest_utm.northing = dem->min_north;
474 dem_southwest_utm.easting = dem->min_east;
475 dem_northeast_utm.zone = dem_southwest_utm.zone = dem->utm_zone;
476 dem_northeast_utm.letter = dem_southwest_utm.letter = dem->utm_letter;
478 a_coords_utm_to_latlon(&dem_northeast_utm, &dem_northeast);
479 a_coords_utm_to_latlon(&dem_southwest_utm, &dem_southwest);
482 if ( (max_lat > dem_northeast.lat && min_lat > dem_northeast.lat) ||
483 (max_lat < dem_southwest.lat && min_lat < dem_southwest.lat) )
485 else if ( (max_lon > dem_northeast.lon && min_lon > dem_northeast.lon) ||
486 (max_lon < dem_southwest.lon && min_lon < dem_southwest.lon) )
488 /* else they overlap */
490 /**** End Overlap Check ****/
491 /* boxes to show where we have DEM instead of actually drawing the DEM.
492 * useful if we want to see what areas we have coverage for (if we want
493 * to get elevation data for a track) but don't want to cover the map.
497 /* draw a box if a DEM is loaded. in future I'd like to add an option for this
498 * this is useful if we want to see what areas we have dem for but don't want to
499 * cover the map (or maybe we just need translucent DEM?) */
501 VikCoord demne, demsw;
503 vik_coord_load_from_latlon(&demne, vik_viewport_get_coord_mode(vp), &dem_northeast);
504 vik_coord_load_from_latlon(&demsw, vik_viewport_get_coord_mode(vp), &dem_southwest);
506 vik_viewport_coord_to_screen ( vp, &demne, &x1, &y1 );
507 vik_viewport_coord_to_screen ( vp, &demsw, &x2, &y2 );
509 if ( x1 > vik_viewport_get_width(vp) ) x1=vik_viewport_get_width(vp);
510 if ( y2 > vik_viewport_get_height(vp) ) y2=vik_viewport_get_height(vp);
511 if ( x2 < 0 ) x2 = 0;
512 if ( y1 < 0 ) y1 = 0;
513 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
514 FALSE, x2, y1, x1-x2, y2-y1 );
519 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
520 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
522 gdouble max_lat_as, max_lon_as, min_lat_as, min_lon_as;
523 gdouble start_lat_as, end_lat_as, start_lon_as, end_lon_as;
525 gdouble start_lat, end_lat, start_lon, end_lon;
527 struct LatLon counter;
529 guint x, y, start_x, start_y;
533 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 80 ); /* todo: smarter calculation. */
535 gdouble nscale_deg = dem->north_scale / ((gdouble) 3600);
536 gdouble escale_deg = dem->east_scale / ((gdouble) 3600);
538 max_lat_as = max_lat * 3600;
539 min_lat_as = min_lat * 3600;
540 max_lon_as = max_lon * 3600;
541 min_lon_as = min_lon * 3600;
543 start_lat_as = MAX(min_lat_as, dem->min_north);
544 end_lat_as = MIN(max_lat_as, dem->max_north);
545 start_lon_as = MAX(min_lon_as, dem->min_east);
546 end_lon_as = MIN(max_lon_as, dem->max_east);
548 start_lat = floor(start_lat_as / dem->north_scale) * nscale_deg;
549 end_lat = ceil (end_lat_as / dem->north_scale) * nscale_deg;
550 start_lon = floor(start_lon_as / dem->east_scale) * escale_deg;
551 end_lon = ceil (end_lon_as / dem->east_scale) * escale_deg;
553 vik_dem_east_north_to_xy ( dem, start_lon_as, start_lat_as, &start_x, &start_y );
554 guint gradient_skip_factor = 1;
555 if(vdl->type == DEM_TYPE_GRADIENT)
556 gradient_skip_factor = skip_factor;
558 /* verify sane elev interval */
559 if ( vdl->max_elev <= vdl->min_elev )
560 vdl->max_elev = vdl->min_elev + 1;
562 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 ) {
563 // NOTE: ( counter.lon <= end_lon + ESCALE_DEG*SKIP_FACTOR ) is neccessary so in high zoom modes,
564 // the leftmost column does also get drawn, if the center point is out of viewport.
565 if ( x >= 0 && x < dem->n_columns ) {
566 column = g_ptr_array_index ( dem->columns, x );
567 // get previous and next column. catch out-of-bound.
569 new_x -= gradient_skip_factor;
571 prevcolumn = g_ptr_array_index ( dem->columns, x+1);
573 prevcolumn = g_ptr_array_index ( dem->columns, new_x);
575 new_x += gradient_skip_factor;
576 if(new_x >= dem->n_columns)
577 nextcolumn = g_ptr_array_index ( dem->columns, x-1);
579 nextcolumn = g_ptr_array_index ( dem->columns, new_x);
581 for ( y=start_y, counter.lat = start_lat; counter.lat <= end_lat; counter.lat += nscale_deg * skip_factor, y += skip_factor ) {
582 if ( y > column->n_points )
585 elev = column->points[y];
587 // calculate bounding box for drawing
588 gint box_x, box_y, box_width, box_height;
591 box_c.lat += (nscale_deg * skip_factor)/2;
592 box_c.lon -= (escale_deg * skip_factor)/2;
593 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
594 vik_viewport_coord_to_screen(vp, &tmp, &box_x, &box_y);
595 // catch box at borders
600 box_c.lat -= nscale_deg * skip_factor;
601 box_c.lon += escale_deg * skip_factor;
602 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
603 vik_viewport_coord_to_screen(vp, &tmp, &box_width, &box_height);
606 // catch box at borders
607 if(box_width < 0 || box_height < 0)
608 continue; // skip this. this is out of our viewport anyway. FIXME: why?
610 gboolean below_minimum = FALSE;
611 if(vdl->type == DEM_TYPE_HEIGHT) {
612 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev ) {
613 // Prevent 'elev - vdl->min_elev' from being negative so can safely use as array index
614 elev = ceil ( vdl->min_elev );
615 below_minimum = TRUE;
617 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
618 elev = vdl->max_elev;
622 if(box_width < 0 || box_height < 0) // FIXME: why does this happen?
625 if(vdl->type == DEM_TYPE_GRADIENT) {
626 if( elev == VIK_DEM_INVALID_ELEVATION ) {
629 // calculate and sum gradient in all directions
633 // calculate gradient from height points all around the current one
634 new_y = y - gradient_skip_factor;
637 change += get_height_difference(elev, prevcolumn->points[new_y]);
638 change += get_height_difference(elev, column->points[new_y]);
639 change += get_height_difference(elev, nextcolumn->points[new_y]);
641 change += get_height_difference(elev, prevcolumn->points[y]);
642 change += get_height_difference(elev, nextcolumn->points[y]);
644 new_y = y + gradient_skip_factor;
645 if(new_y >= column->n_points)
647 change += get_height_difference(elev, prevcolumn->points[new_y]);
648 change += get_height_difference(elev, column->points[new_y]);
649 change += get_height_difference(elev, nextcolumn->points[new_y]);
651 change = change / ((skip_factor > 1) ? log(skip_factor) : 0.55); // FIXME: better calc.
653 if(change < vdl->min_elev)
654 // Prevent 'change - vdl->min_elev' from being negative so can safely use as array index
655 change = ceil ( vdl->min_elev );
657 if(change > vdl->max_elev)
658 change = vdl->max_elev;
660 // void vik_viewport_draw_rectangle ( VikViewport *vvp, GdkGC *gc, gboolean filled, gint x1, gint y1, gint x2, gint y2 );
661 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);
664 if(vdl->type == DEM_TYPE_HEIGHT) {
665 if ( elev == VIK_DEM_INVALID_ELEVATION )
666 ; /* don't draw it */
667 else if ( elev <= 0 || below_minimum )
668 /* If 'sea' colour or below the defined mininum draw in the configurable colour */
669 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, box_x, box_y, box_width, box_height);
671 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);
678 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
679 gdouble max_nor, max_eas, min_nor, min_eas;
680 gdouble start_nor, start_eas, end_nor, end_eas;
684 guint x, y, start_x, start_y;
686 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
689 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 10 ); /* todo: smarter calculation. */
691 VikCoord tleft, tright, bleft, bright;
693 vik_viewport_screen_to_coord ( vp, 0, 0, &tleft );
694 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), 0, &tright );
695 vik_viewport_screen_to_coord ( vp, 0, vik_viewport_get_height(vp), &bleft );
696 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), vik_viewport_get_height(vp), &bright );
699 vik_coord_convert(&tleft, VIK_COORD_UTM);
700 vik_coord_convert(&tright, VIK_COORD_UTM);
701 vik_coord_convert(&bleft, VIK_COORD_UTM);
702 vik_coord_convert(&bright, VIK_COORD_UTM);
704 max_nor = MAX(tleft.north_south, tright.north_south);
705 min_nor = MIN(bleft.north_south, bright.north_south);
706 max_eas = MAX(bright.east_west, tright.east_west);
707 min_eas = MIN(bleft.east_west, tleft.east_west);
709 start_nor = MAX(min_nor, dem->min_north);
710 end_nor = MIN(max_nor, dem->max_north);
711 if ( tleft.utm_zone == dem->utm_zone && bleft.utm_zone == dem->utm_zone
712 && (tleft.utm_letter >= 'N') == (dem->utm_letter >= 'N')
713 && (bleft.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
714 start_eas = MAX(min_eas, dem->min_east);
716 start_eas = dem->min_east;
717 if ( tright.utm_zone == dem->utm_zone && bright.utm_zone == dem->utm_zone
718 && (tright.utm_letter >= 'N') == (dem->utm_letter >= 'N')
719 && (bright.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
720 end_eas = MIN(max_eas, dem->max_east);
722 end_eas = dem->max_east;
724 start_nor = floor(start_nor / dem->north_scale) * dem->north_scale;
725 end_nor = ceil (end_nor / dem->north_scale) * dem->north_scale;
726 start_eas = floor(start_eas / dem->east_scale) * dem->east_scale;
727 end_eas = ceil (end_eas / dem->east_scale) * dem->east_scale;
729 vik_dem_east_north_to_xy ( dem, start_eas, start_nor, &start_x, &start_y );
731 /* TODO: why start_x and start_y are -1 -- rounding error from above? */
733 counter.zone = dem->utm_zone;
734 counter.letter = dem->utm_letter;
736 for ( x=start_x, counter.easting = start_eas; counter.easting <= end_eas; counter.easting += dem->east_scale * skip_factor, x += skip_factor ) {
737 if ( x > 0 && x < dem->n_columns ) {
738 column = g_ptr_array_index ( dem->columns, x );
739 for ( y=start_y, counter.northing = start_nor; counter.northing <= end_nor; counter.northing += dem->north_scale * skip_factor, y += skip_factor ) {
740 if ( y > column->n_points )
742 elev = column->points[y];
743 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev )
745 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
750 vik_coord_load_from_utm(&tmp, vik_viewport_get_coord_mode(vp), &counter);
751 vik_viewport_coord_to_screen(vp, &tmp, &a, &b);
752 if ( elev == VIK_DEM_INVALID_ELEVATION )
753 ; /* don't draw it */
754 else if ( elev <= 0 )
755 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, a-1, b-1, 2, 2 );
757 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 );
765 /* return the continent for the specified lat, lon */
767 static const gchar *srtm_continent_dir ( gint lat, gint lon )
769 extern const char *_srtm_continent_data[];
770 static GHashTable *srtm_continent = NULL;
771 const gchar *continent;
774 if (!srtm_continent) {
777 srtm_continent = g_hash_table_new(g_str_hash, g_str_equal);
778 s = _srtm_continent_data;
779 while (*s != (gchar *)-1) {
782 g_hash_table_insert(srtm_continent, (gpointer) *s, (gpointer) continent);
788 g_snprintf(name, sizeof(name), "%c%02d%c%03d",
789 (lat >= 0) ? 'N' : 'S', ABS(lat),
790 (lon >= 0) ? 'E' : 'W', ABS(lon));
792 return(g_hash_table_lookup(srtm_continent, name));
795 void vik_dem_layer_draw ( VikDEMLayer *vdl, gpointer data )
797 VikViewport *vp = (VikViewport *) data;
798 GList *dems_iter = vdl->files;
802 /* search for SRTM3 90m */
804 if ( vdl->source == DEM_SOURCE_SRTM )
805 srtm_draw_existence ( vp );
806 #ifdef VIK_CONFIG_DEM24K
807 else if ( vdl->source == DEM_SOURCE_DEM24K )
808 dem24k_draw_existence ( vp );
811 while ( dems_iter ) {
812 dem = a_dems_get ( (const char *) (dems_iter->data) );
814 vik_dem_layer_draw_dem ( vdl, vp, dem );
815 dems_iter = dems_iter->next;
819 void vik_dem_layer_free ( VikDEMLayer *vdl )
822 if ( vdl->color != NULL )
823 g_object_unref ( vdl->color );
826 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
827 g_object_unref ( vdl->gcs[i] );
830 if ( vdl->gcsgradient )
831 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
832 g_object_unref ( vdl->gcsgradient[i] );
833 g_free ( vdl->gcsgradient );
835 a_dems_list_free ( vdl->files );
838 VikDEMLayer *vik_dem_layer_create ( VikViewport *vp )
840 VikDEMLayer *vdl = vik_dem_layer_new ();
843 /* TODO: share GCS between layers */
844 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
845 vdl->gcs[i] = vik_viewport_new_gc ( vp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
847 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
848 vdl->gcsgradient[i] = vik_viewport_new_gc ( vp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
852 /**************************************************************
853 **** SOURCES & DOWNLOADING
854 **************************************************************/
860 VikDEMLayer *vdl; /* NULL if not alive */
866 /**************************************************
868 **************************************************/
870 static void srtm_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
873 const gchar *continent_dir;
875 intlat = (int)floor(p->lat);
876 intlon = (int)floor(p->lon);
877 continent_dir = srtm_continent_dir(intlat, intlon);
879 if (!continent_dir) {
880 g_warning(N_("No SRTM data available for %f, %f"), p->lat, p->lon);
884 gchar *src_fn = g_strdup_printf("%s%s/%c%02d%c%03d.hgt.zip",
887 (intlat >= 0) ? 'N' : 'S',
889 (intlon >= 0) ? 'E' : 'W',
892 static DownloadMapOptions options = { FALSE, FALSE, NULL, 0, a_check_map_file };
893 a_http_download_get_url ( SRTM_HTTP_SITE, src_fn, p->dest, &options, NULL );
897 static gchar *srtm_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
900 const gchar *continent_dir;
902 intlat = (int)floor(lat);
903 intlon = (int)floor(lon);
904 continent_dir = srtm_continent_dir(intlat, intlon);
907 continent_dir = "nowhere";
909 return g_strdup_printf("srtm3-%s%s%c%02d%c%03d.hgt.zip",
912 (intlat >= 0) ? 'N' : 'S',
914 (intlon >= 0) ? 'E' : 'W',
919 /* TODO: generalize */
920 static void srtm_draw_existence ( VikViewport *vp )
922 gdouble max_lat, max_lon, min_lat, min_lon;
923 gchar buf[strlen(MAPS_CACHE_DIR)+strlen(SRTM_CACHE_TEMPLATE)+30];
926 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
928 for (i = floor(min_lat); i <= floor(max_lat); i++) {
929 for (j = floor(min_lon); j <= floor(max_lon); j++) {
930 const gchar *continent_dir;
931 if ((continent_dir = srtm_continent_dir(i, j)) == NULL)
933 g_snprintf(buf, sizeof(buf), SRTM_CACHE_TEMPLATE,
937 (i >= 0) ? 'N' : 'S',
939 (j >= 0) ? 'E' : 'W',
941 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
946 sw.mode = VIK_COORD_LATLON;
947 ne.north_south = i+1;
949 ne.mode = VIK_COORD_LATLON;
950 vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
951 vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
952 if ( x1 < 0 ) x1 = 0;
953 if ( y2 < 0 ) y2 = 0;
954 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
955 FALSE, x1, y2, x2-x1, y1-y2 );
962 /**************************************************
964 **************************************************/
966 #ifdef VIK_CONFIG_DEM24K
968 static void dem24k_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
971 gchar *cmdline = g_strdup_printf("%s %.03f %.03f",
972 DEM24K_DOWNLOAD_SCRIPT,
975 /* FIX: don't use system, use execv or something. check for existence */
979 static gchar *dem24k_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
981 return g_strdup_printf("dem24k/%d/%d/%.03f,%.03f.dem",
988 /* TODO: generalize */
989 static void dem24k_draw_existence ( VikViewport *vp )
991 gdouble max_lat, max_lon, min_lat, min_lon;
992 gchar buf[strlen(MAPS_CACHE_DIR)+40];
995 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
997 for (i = floor(min_lat*8)/8; i <= floor(max_lat*8)/8; i+=0.125) {
998 /* check lat dir first -- faster */
999 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/",
1002 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1004 for (j = floor(min_lon*8)/8; j <= floor(max_lon*8)/8; j+=0.125) {
1005 /* check lon dir first -- faster */
1006 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/",
1010 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1012 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/%.03f,%.03f.dem",
1018 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1020 gint x1, y1, x2, y2;
1022 sw.east_west = j-0.125;
1023 sw.mode = VIK_COORD_LATLON;
1024 ne.north_south = i+0.125;
1026 ne.mode = VIK_COORD_LATLON;
1027 vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
1028 vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
1029 if ( x1 < 0 ) x1 = 0;
1030 if ( y2 < 0 ) y2 = 0;
1031 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
1032 FALSE, x1, y2, x2-x1, y1-y2 );
1039 /**************************************************
1040 * SOURCES -- DOWNLOADING & IMPORTING TOOL *
1041 **************************************************
1044 static void weak_ref_cb ( gpointer ptr, GObject * dead_vdl )
1046 DEMDownloadParams *p = ptr;
1047 g_mutex_lock ( p->mutex );
1049 g_mutex_unlock ( p->mutex );
1052 /* Try to add file full_path.
1053 * full_path will be copied.
1054 * returns FALSE if file does not exists, TRUE otherwise.
1056 static gboolean dem_layer_add_file ( VikDEMLayer *vdl, const gchar *full_path )
1058 if ( g_file_test(full_path, G_FILE_TEST_EXISTS ) == TRUE ) {
1059 /* only load if file size is not 0 (not in progress) */
1061 stat (full_path, &sb);
1063 gchar *duped_path = g_strdup(full_path);
1064 vdl->files = g_list_prepend ( vdl->files, duped_path );
1065 a_dems_load ( duped_path );
1066 g_debug("%s: %s", __FUNCTION__, duped_path);
1067 vik_layer_emit_update ( VIK_LAYER(vdl) );
1074 static void dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
1076 if ( p->source == DEM_SOURCE_SRTM )
1077 srtm_dem_download_thread ( p, threaddata );
1078 #ifdef VIK_CONFIG_DEM24K
1079 else if ( p->source == DEM_SOURCE_DEM24K )
1080 dem24k_dem_download_thread ( p, threaddata );
1083 gdk_threads_enter();
1084 g_mutex_lock ( p->mutex );
1086 g_object_weak_unref ( G_OBJECT(p->vdl), weak_ref_cb, p );
1088 if ( dem_layer_add_file ( p->vdl, p->dest ) )
1089 vik_layer_emit_update ( VIK_LAYER(p->vdl) );
1091 g_mutex_unlock ( p->mutex );
1092 gdk_threads_leave();
1096 static void free_dem_download_params ( DEMDownloadParams *p )
1098 g_mutex_free ( p->mutex );
1103 static gpointer dem_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1109 static gboolean dem_layer_download_release ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1115 gchar *dem_file = NULL;
1117 if ( vdl->source == DEM_SOURCE_NONE )
1118 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vdl), _("No download source selected. Edit layer properties.") );
1120 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1121 vik_coord_to_latlon ( &coord, &ll );
1124 if ( vdl->source == DEM_SOURCE_SRTM )
1125 dem_file = srtm_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1126 #ifdef VIK_CONFIG_DEM24K
1127 else if ( vdl->source == DEM_SOURCE_DEM24K )
1128 dem_file = dem24k_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1134 full_path = g_strdup_printf("%s%s", MAPS_CACHE_DIR, dem_file );
1136 g_debug("%s: %s", __FUNCTION__, full_path);
1138 // TODO: check if already in filelist
1140 if ( ! dem_layer_add_file(vdl, full_path) ) {
1141 gchar *tmp = g_strdup_printf ( _("Downloading DEM %s"), dem_file );
1142 DEMDownloadParams *p = g_malloc(sizeof(DEMDownloadParams));
1143 p->dest = g_strdup(full_path);
1147 p->mutex = g_mutex_new();
1148 p->source = vdl->source;
1149 g_object_weak_ref(G_OBJECT(p->vdl), weak_ref_cb, p );
1151 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vdl), tmp,
1152 (vik_thr_func) dem_download_thread, p,
1153 (vik_thr_free_func) free_dem_download_params, NULL, 1 );
1158 g_free ( dem_file );
1159 g_free ( full_path );
1164 static gboolean dem_layer_download_click ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1166 /* choose & keep track of cache dir
1167 * download in background thread
1168 * download over area */