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,
237 struct _VikDEMLayer {
249 GType vik_dem_layer_get_type ()
251 static GType vdl_type = 0;
255 static const GTypeInfo vdl_info =
257 sizeof (VikDEMLayerClass),
258 NULL, /* base_init */
259 NULL, /* base_finalize */
260 NULL, /* class init */
261 NULL, /* class_finalize */
262 NULL, /* class_data */
263 sizeof (VikDEMLayer),
265 NULL /* instance init */
267 vdl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikDEMLayer", &vdl_info, 0 );
273 static const gchar* dem_layer_tooltip( VikDEMLayer *vdl )
275 static gchar tmp_buf[100];
276 g_snprintf (tmp_buf, sizeof(tmp_buf), _("Number of files: %d"), g_list_length (vdl->files));
280 static void dem_layer_marshall( VikDEMLayer *vdl, guint8 **data, gint *len )
282 vik_layer_marshall_params ( VIK_LAYER(vdl), data, len );
285 static VikDEMLayer *dem_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
287 VikDEMLayer *rv = vik_dem_layer_new ();
290 /* TODO: share GCS between layers */
291 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
292 rv->gcs[i] = vik_viewport_new_gc ( vvp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
294 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
295 rv->gcsgradient[i] = vik_viewport_new_gc ( vvp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
297 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
301 /* Structure for DEM data used in background thread */
304 } dem_load_thread_data;
307 * Function for starting the DEM file loading as a background thread
309 static int dem_layer_load_list_thread ( dem_load_thread_data *dltd, gpointer threaddata )
311 int result = 0; // Default to good
313 if ( a_dems_load_list ( &(dltd->vdl->files), threaddata ) ) {
318 // ATM as each file is processed the screen is not updated (no mechanism exposed to a_dems_load_list)
319 // Thus force draw only at the end, as loading is complete/aborted
321 // Test is helpful to prevent Gtk-CRITICAL warnings if the program is exitted whilst loading
322 if ( IS_VIK_LAYER(dltd->vdl) )
323 vik_layer_emit_update ( VIK_LAYER(dltd->vdl) );
329 static void dem_layer_thread_data_free ( dem_load_thread_data *data )
335 static void dem_layer_thread_cancel ( dem_load_thread_data *data )
338 // Instead of freeing the list, leave it as partially processed
339 // Thus we can see/use what was done
342 gboolean dem_layer_set_param ( VikDEMLayer *vdl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
346 case PARAM_COLOR: gdk_gc_set_rgb_fg_color ( vdl->gcs[0], &(data.c) ); break;
347 case PARAM_SOURCE: vdl->source = data.u; break;
348 case PARAM_TYPE: vdl->type = data.u; break;
350 /* Convert to store internally
351 NB file operation always in internal units (metres) */
352 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
353 vdl->min_elev = VIK_FEET_TO_METERS(data.d);
355 vdl->min_elev = data.d;
358 /* Convert to store internally
359 NB file operation always in internal units (metres) */
360 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
361 vdl->max_elev = VIK_FEET_TO_METERS(data.d);
363 vdl->max_elev = data.d;
367 // Clear out old settings - if any commonalities with new settings they will have to be read again
368 a_dems_list_free ( vdl->files );
369 // Set file list so any other intermediate screen drawing updates will show currently loaded DEMs by the working thread
370 vdl->files = data.sl;
372 dem_load_thread_data *dltd = g_malloc ( sizeof(dem_load_thread_data) );
374 dltd->vdl->files = data.sl;
376 a_background_thread ( VIK_GTK_WINDOW_FROM_WIDGET(vp),
378 (vik_thr_func) dem_layer_load_list_thread,
380 (vik_thr_free_func) dem_layer_thread_data_free,
381 (vik_thr_free_func) dem_layer_thread_cancel,
382 g_list_length ( data.sl ) ); // Number of DEM files
389 static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gboolean is_file_operation )
391 VikLayerParamData rv;
394 case PARAM_FILES: rv.sl = vdl->files; break;
395 case PARAM_SOURCE: rv.u = vdl->source; break;
396 case PARAM_TYPE: rv.u = vdl->type; break;
397 case PARAM_COLOR: vik_gc_get_fg_color ( vdl->gcs[0], &(rv.c) ); break;
399 /* Convert for display in desired units
400 NB file operation always in internal units (metres) */
401 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
402 rv.d = VIK_METERS_TO_FEET(vdl->min_elev);
404 rv.d = vdl->min_elev;
407 /* Convert for display in desired units
408 NB file operation always in internal units (metres) */
409 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
410 rv.d = VIK_METERS_TO_FEET(vdl->max_elev);
412 rv.d = vdl->max_elev;
418 static void dem_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file )
420 /* nothing ATM, but keep in case it's needed the future */
423 VikDEMLayer *vik_dem_layer_new ( )
425 VikDEMLayer *vdl = VIK_DEM_LAYER ( g_object_new ( VIK_DEM_LAYER_TYPE, NULL ) );
427 vik_layer_init ( VIK_LAYER(vdl), VIK_LAYER_DEM );
431 vdl->gcs = g_malloc(sizeof(GdkGC *)*DEM_N_HEIGHT_COLORS);
432 vdl->gcsgradient = g_malloc(sizeof(GdkGC *)*DEM_N_GRADIENT_COLORS);
433 /* make new gcs only if we need it (copy layer -> use old) */
436 vdl->max_elev = 1000.0;
437 vdl->source = DEM_SOURCE_SRTM;
438 vdl->type = DEM_TYPE_HEIGHT;
443 static inline guint16 get_height_difference(gint16 elev, gint16 new_elev)
445 if(new_elev == VIK_DEM_INVALID_ELEVATION)
448 return abs(new_elev - elev);
452 static void vik_dem_layer_draw_dem ( VikDEMLayer *vdl, VikViewport *vp, VikDEM *dem )
454 VikDEMColumn *column, *prevcolumn, *nextcolumn;
456 struct LatLon dem_northeast, dem_southwest;
457 gdouble max_lat, max_lon, min_lat, min_lon;
459 /**** Check if viewport and DEM data overlap ****/
461 /* get min, max lat/lon of viewport */
462 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
464 /* get min, max lat/lon of DEM data */
465 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
466 dem_northeast.lat = dem->max_north / 3600.0;
467 dem_northeast.lon = dem->max_east / 3600.0;
468 dem_southwest.lat = dem->min_north / 3600.0;
469 dem_southwest.lon = dem->min_east / 3600.0;
470 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
471 struct UTM dem_northeast_utm, dem_southwest_utm;
472 dem_northeast_utm.northing = dem->max_north;
473 dem_northeast_utm.easting = dem->max_east;
474 dem_southwest_utm.northing = dem->min_north;
475 dem_southwest_utm.easting = dem->min_east;
476 dem_northeast_utm.zone = dem_southwest_utm.zone = dem->utm_zone;
477 dem_northeast_utm.letter = dem_southwest_utm.letter = dem->utm_letter;
479 a_coords_utm_to_latlon(&dem_northeast_utm, &dem_northeast);
480 a_coords_utm_to_latlon(&dem_southwest_utm, &dem_southwest);
483 if ( (max_lat > dem_northeast.lat && min_lat > dem_northeast.lat) ||
484 (max_lat < dem_southwest.lat && min_lat < dem_southwest.lat) )
486 else if ( (max_lon > dem_northeast.lon && min_lon > dem_northeast.lon) ||
487 (max_lon < dem_southwest.lon && min_lon < dem_southwest.lon) )
489 /* else they overlap */
491 /**** End Overlap Check ****/
492 /* boxes to show where we have DEM instead of actually drawing the DEM.
493 * useful if we want to see what areas we have coverage for (if we want
494 * to get elevation data for a track) but don't want to cover the map.
498 /* draw a box if a DEM is loaded. in future I'd like to add an option for this
499 * this is useful if we want to see what areas we have dem for but don't want to
500 * cover the map (or maybe we just need translucent DEM?) */
502 VikCoord demne, demsw;
504 vik_coord_load_from_latlon(&demne, vik_viewport_get_coord_mode(vp), &dem_northeast);
505 vik_coord_load_from_latlon(&demsw, vik_viewport_get_coord_mode(vp), &dem_southwest);
507 vik_viewport_coord_to_screen ( vp, &demne, &x1, &y1 );
508 vik_viewport_coord_to_screen ( vp, &demsw, &x2, &y2 );
510 if ( x1 > vik_viewport_get_width(vp) ) x1=vik_viewport_get_width(vp);
511 if ( y2 > vik_viewport_get_height(vp) ) y2=vik_viewport_get_height(vp);
512 if ( x2 < 0 ) x2 = 0;
513 if ( y1 < 0 ) y1 = 0;
514 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
515 FALSE, x2, y1, x1-x2, y2-y1 );
520 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
521 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
523 gdouble max_lat_as, max_lon_as, min_lat_as, min_lon_as;
524 gdouble start_lat_as, end_lat_as, start_lon_as, end_lon_as;
526 gdouble start_lat, end_lat, start_lon, end_lon;
528 struct LatLon counter;
530 guint x, y, start_x, start_y;
534 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 80 ); /* todo: smarter calculation. */
536 gdouble nscale_deg = dem->north_scale / ((gdouble) 3600);
537 gdouble escale_deg = dem->east_scale / ((gdouble) 3600);
539 max_lat_as = max_lat * 3600;
540 min_lat_as = min_lat * 3600;
541 max_lon_as = max_lon * 3600;
542 min_lon_as = min_lon * 3600;
544 start_lat_as = MAX(min_lat_as, dem->min_north);
545 end_lat_as = MIN(max_lat_as, dem->max_north);
546 start_lon_as = MAX(min_lon_as, dem->min_east);
547 end_lon_as = MIN(max_lon_as, dem->max_east);
549 start_lat = floor(start_lat_as / dem->north_scale) * nscale_deg;
550 end_lat = ceil (end_lat_as / dem->north_scale) * nscale_deg;
551 start_lon = floor(start_lon_as / dem->east_scale) * escale_deg;
552 end_lon = ceil (end_lon_as / dem->east_scale) * escale_deg;
554 vik_dem_east_north_to_xy ( dem, start_lon_as, start_lat_as, &start_x, &start_y );
555 guint gradient_skip_factor = 1;
556 if(vdl->type == DEM_TYPE_GRADIENT)
557 gradient_skip_factor = skip_factor;
559 /* verify sane elev interval */
560 if ( vdl->max_elev <= vdl->min_elev )
561 vdl->max_elev = vdl->min_elev + 1;
563 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 ) {
564 // NOTE: ( counter.lon <= end_lon + ESCALE_DEG*SKIP_FACTOR ) is neccessary so in high zoom modes,
565 // the leftmost column does also get drawn, if the center point is out of viewport.
566 if ( x >= 0 && x < dem->n_columns ) {
567 column = g_ptr_array_index ( dem->columns, x );
568 // get previous and next column. catch out-of-bound.
570 new_x -= gradient_skip_factor;
572 prevcolumn = g_ptr_array_index ( dem->columns, x+1);
574 prevcolumn = g_ptr_array_index ( dem->columns, new_x);
576 new_x += gradient_skip_factor;
577 if(new_x >= dem->n_columns)
578 nextcolumn = g_ptr_array_index ( dem->columns, x-1);
580 nextcolumn = g_ptr_array_index ( dem->columns, new_x);
582 for ( y=start_y, counter.lat = start_lat; counter.lat <= end_lat; counter.lat += nscale_deg * skip_factor, y += skip_factor ) {
583 if ( y > column->n_points )
586 elev = column->points[y];
588 // calculate bounding box for drawing
589 gint box_x, box_y, box_width, box_height;
592 box_c.lat += (nscale_deg * skip_factor)/2;
593 box_c.lon -= (escale_deg * skip_factor)/2;
594 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
595 vik_viewport_coord_to_screen(vp, &tmp, &box_x, &box_y);
596 // catch box at borders
601 box_c.lat -= nscale_deg * skip_factor;
602 box_c.lon += escale_deg * skip_factor;
603 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
604 vik_viewport_coord_to_screen(vp, &tmp, &box_width, &box_height);
607 // catch box at borders
608 if(box_width < 0 || box_height < 0)
609 continue; // skip this. this is out of our viewport anyway. FIXME: why?
611 gboolean below_minimum = FALSE;
612 if(vdl->type == DEM_TYPE_HEIGHT) {
613 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev ) {
614 // Prevent 'elev - vdl->min_elev' from being negative so can safely use as array index
615 elev = ceil ( vdl->min_elev );
616 below_minimum = TRUE;
618 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
619 elev = vdl->max_elev;
623 if(box_width < 0 || box_height < 0) // FIXME: why does this happen?
626 if(vdl->type == DEM_TYPE_GRADIENT) {
627 if( elev == VIK_DEM_INVALID_ELEVATION ) {
630 // calculate and sum gradient in all directions
634 // calculate gradient from height points all around the current one
635 new_y = y - gradient_skip_factor;
638 change += get_height_difference(elev, prevcolumn->points[new_y]);
639 change += get_height_difference(elev, column->points[new_y]);
640 change += get_height_difference(elev, nextcolumn->points[new_y]);
642 change += get_height_difference(elev, prevcolumn->points[y]);
643 change += get_height_difference(elev, nextcolumn->points[y]);
645 new_y = y + gradient_skip_factor;
646 if(new_y >= column->n_points)
648 change += get_height_difference(elev, prevcolumn->points[new_y]);
649 change += get_height_difference(elev, column->points[new_y]);
650 change += get_height_difference(elev, nextcolumn->points[new_y]);
652 change = change / ((skip_factor > 1) ? log(skip_factor) : 0.55); // FIXME: better calc.
654 if(change < vdl->min_elev)
655 // Prevent 'change - vdl->min_elev' from being negative so can safely use as array index
656 change = ceil ( vdl->min_elev );
658 if(change > vdl->max_elev)
659 change = vdl->max_elev;
661 // void vik_viewport_draw_rectangle ( VikViewport *vvp, GdkGC *gc, gboolean filled, gint x1, gint y1, gint x2, gint y2 );
662 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);
665 if(vdl->type == DEM_TYPE_HEIGHT) {
666 if ( elev == VIK_DEM_INVALID_ELEVATION )
667 ; /* don't draw it */
668 else if ( elev <= 0 || below_minimum )
669 /* If 'sea' colour or below the defined mininum draw in the configurable colour */
670 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, box_x, box_y, box_width, box_height);
672 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);
679 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
680 gdouble max_nor, max_eas, min_nor, min_eas;
681 gdouble start_nor, start_eas, end_nor, end_eas;
685 guint x, y, start_x, start_y;
687 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
690 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 10 ); /* todo: smarter calculation. */
692 VikCoord tleft, tright, bleft, bright;
694 vik_viewport_screen_to_coord ( vp, 0, 0, &tleft );
695 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), 0, &tright );
696 vik_viewport_screen_to_coord ( vp, 0, vik_viewport_get_height(vp), &bleft );
697 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), vik_viewport_get_height(vp), &bright );
700 vik_coord_convert(&tleft, VIK_COORD_UTM);
701 vik_coord_convert(&tright, VIK_COORD_UTM);
702 vik_coord_convert(&bleft, VIK_COORD_UTM);
703 vik_coord_convert(&bright, VIK_COORD_UTM);
705 max_nor = MAX(tleft.north_south, tright.north_south);
706 min_nor = MIN(bleft.north_south, bright.north_south);
707 max_eas = MAX(bright.east_west, tright.east_west);
708 min_eas = MIN(bleft.east_west, tleft.east_west);
710 start_nor = MAX(min_nor, dem->min_north);
711 end_nor = MIN(max_nor, dem->max_north);
712 if ( tleft.utm_zone == dem->utm_zone && bleft.utm_zone == dem->utm_zone
713 && (tleft.utm_letter >= 'N') == (dem->utm_letter >= 'N')
714 && (bleft.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
715 start_eas = MAX(min_eas, dem->min_east);
717 start_eas = dem->min_east;
718 if ( tright.utm_zone == dem->utm_zone && bright.utm_zone == dem->utm_zone
719 && (tright.utm_letter >= 'N') == (dem->utm_letter >= 'N')
720 && (bright.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
721 end_eas = MIN(max_eas, dem->max_east);
723 end_eas = dem->max_east;
725 start_nor = floor(start_nor / dem->north_scale) * dem->north_scale;
726 end_nor = ceil (end_nor / dem->north_scale) * dem->north_scale;
727 start_eas = floor(start_eas / dem->east_scale) * dem->east_scale;
728 end_eas = ceil (end_eas / dem->east_scale) * dem->east_scale;
730 vik_dem_east_north_to_xy ( dem, start_eas, start_nor, &start_x, &start_y );
732 /* TODO: why start_x and start_y are -1 -- rounding error from above? */
734 counter.zone = dem->utm_zone;
735 counter.letter = dem->utm_letter;
737 for ( x=start_x, counter.easting = start_eas; counter.easting <= end_eas; counter.easting += dem->east_scale * skip_factor, x += skip_factor ) {
738 if ( x > 0 && x < dem->n_columns ) {
739 column = g_ptr_array_index ( dem->columns, x );
740 for ( y=start_y, counter.northing = start_nor; counter.northing <= end_nor; counter.northing += dem->north_scale * skip_factor, y += skip_factor ) {
741 if ( y > column->n_points )
743 elev = column->points[y];
744 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev )
746 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
751 vik_coord_load_from_utm(&tmp, vik_viewport_get_coord_mode(vp), &counter);
752 vik_viewport_coord_to_screen(vp, &tmp, &a, &b);
753 if ( elev == VIK_DEM_INVALID_ELEVATION )
754 ; /* don't draw it */
755 else if ( elev <= 0 )
756 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, a-1, b-1, 2, 2 );
758 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 );
766 /* return the continent for the specified lat, lon */
768 static const gchar *srtm_continent_dir ( gint lat, gint lon )
770 extern const char *_srtm_continent_data[];
771 static GHashTable *srtm_continent = NULL;
772 const gchar *continent;
775 if (!srtm_continent) {
778 srtm_continent = g_hash_table_new(g_str_hash, g_str_equal);
779 s = _srtm_continent_data;
780 while (*s != (gchar *)-1) {
783 g_hash_table_insert(srtm_continent, (gpointer) *s, (gpointer) continent);
789 g_snprintf(name, sizeof(name), "%c%02d%c%03d",
790 (lat >= 0) ? 'N' : 'S', ABS(lat),
791 (lon >= 0) ? 'E' : 'W', ABS(lon));
793 return(g_hash_table_lookup(srtm_continent, name));
796 void vik_dem_layer_draw ( VikDEMLayer *vdl, gpointer data )
798 VikViewport *vp = (VikViewport *) data;
799 GList *dems_iter = vdl->files;
803 /* search for SRTM3 90m */
805 if ( vdl->source == DEM_SOURCE_SRTM )
806 srtm_draw_existence ( vp );
807 #ifdef VIK_CONFIG_DEM24K
808 else if ( vdl->source == DEM_SOURCE_DEM24K )
809 dem24k_draw_existence ( vp );
812 while ( dems_iter ) {
813 dem = a_dems_get ( (const char *) (dems_iter->data) );
815 vik_dem_layer_draw_dem ( vdl, vp, dem );
816 dems_iter = dems_iter->next;
820 void vik_dem_layer_free ( VikDEMLayer *vdl )
823 if ( vdl->color != NULL )
824 g_object_unref ( vdl->color );
827 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
828 g_object_unref ( vdl->gcs[i] );
831 if ( vdl->gcsgradient )
832 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
833 g_object_unref ( vdl->gcsgradient[i] );
834 g_free ( vdl->gcsgradient );
836 a_dems_list_free ( vdl->files );
839 VikDEMLayer *vik_dem_layer_create ( VikViewport *vp )
841 VikDEMLayer *vdl = vik_dem_layer_new ();
844 /* TODO: share GCS between layers */
845 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
846 vdl->gcs[i] = vik_viewport_new_gc ( vp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
848 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
849 vdl->gcsgradient[i] = vik_viewport_new_gc ( vp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
853 /**************************************************************
854 **** SOURCES & DOWNLOADING
855 **************************************************************/
861 VikDEMLayer *vdl; /* NULL if not alive */
867 /**************************************************
869 **************************************************/
871 static void srtm_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
874 const gchar *continent_dir;
876 intlat = (int)floor(p->lat);
877 intlon = (int)floor(p->lon);
878 continent_dir = srtm_continent_dir(intlat, intlon);
880 if (!continent_dir) {
881 g_warning(N_("No SRTM data available for %f, %f"), p->lat, p->lon);
885 gchar *src_fn = g_strdup_printf("%s%s/%c%02d%c%03d.hgt.zip",
888 (intlat >= 0) ? 'N' : 'S',
890 (intlon >= 0) ? 'E' : 'W',
893 static DownloadMapOptions options = { FALSE, FALSE, NULL, 0, a_check_map_file };
894 a_http_download_get_url ( SRTM_HTTP_SITE, src_fn, p->dest, &options, NULL );
898 static gchar *srtm_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
901 const gchar *continent_dir;
903 intlat = (int)floor(lat);
904 intlon = (int)floor(lon);
905 continent_dir = srtm_continent_dir(intlat, intlon);
908 continent_dir = "nowhere";
910 return g_strdup_printf("srtm3-%s%s%c%02d%c%03d.hgt.zip",
913 (intlat >= 0) ? 'N' : 'S',
915 (intlon >= 0) ? 'E' : 'W',
920 /* TODO: generalize */
921 static void srtm_draw_existence ( VikViewport *vp )
923 gdouble max_lat, max_lon, min_lat, min_lon;
924 gchar buf[strlen(MAPS_CACHE_DIR)+strlen(SRTM_CACHE_TEMPLATE)+30];
927 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
929 for (i = floor(min_lat); i <= floor(max_lat); i++) {
930 for (j = floor(min_lon); j <= floor(max_lon); j++) {
931 const gchar *continent_dir;
932 if ((continent_dir = srtm_continent_dir(i, j)) == NULL)
934 g_snprintf(buf, sizeof(buf), SRTM_CACHE_TEMPLATE,
938 (i >= 0) ? 'N' : 'S',
940 (j >= 0) ? 'E' : 'W',
942 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
947 sw.mode = VIK_COORD_LATLON;
948 ne.north_south = i+1;
950 ne.mode = VIK_COORD_LATLON;
951 vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
952 vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
953 if ( x1 < 0 ) x1 = 0;
954 if ( y2 < 0 ) y2 = 0;
955 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
956 FALSE, x1, y2, x2-x1, y1-y2 );
963 /**************************************************
965 **************************************************/
967 #ifdef VIK_CONFIG_DEM24K
969 static void dem24k_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
972 gchar *cmdline = g_strdup_printf("%s %.03f %.03f",
973 DEM24K_DOWNLOAD_SCRIPT,
976 /* FIX: don't use system, use execv or something. check for existence */
980 static gchar *dem24k_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
982 return g_strdup_printf("dem24k/%d/%d/%.03f,%.03f.dem",
989 /* TODO: generalize */
990 static void dem24k_draw_existence ( VikViewport *vp )
992 gdouble max_lat, max_lon, min_lat, min_lon;
993 gchar buf[strlen(MAPS_CACHE_DIR)+40];
996 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
998 for (i = floor(min_lat*8)/8; i <= floor(max_lat*8)/8; i+=0.125) {
999 /* check lat dir first -- faster */
1000 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/",
1003 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1005 for (j = floor(min_lon*8)/8; j <= floor(max_lon*8)/8; j+=0.125) {
1006 /* check lon dir first -- faster */
1007 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/",
1011 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1013 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/%.03f,%.03f.dem",
1019 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1021 gint x1, y1, x2, y2;
1023 sw.east_west = j-0.125;
1024 sw.mode = VIK_COORD_LATLON;
1025 ne.north_south = i+0.125;
1027 ne.mode = VIK_COORD_LATLON;
1028 vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
1029 vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
1030 if ( x1 < 0 ) x1 = 0;
1031 if ( y2 < 0 ) y2 = 0;
1032 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
1033 FALSE, x1, y2, x2-x1, y1-y2 );
1040 /**************************************************
1041 * SOURCES -- DOWNLOADING & IMPORTING TOOL *
1042 **************************************************
1045 static void weak_ref_cb ( gpointer ptr, GObject * dead_vdl )
1047 DEMDownloadParams *p = ptr;
1048 g_mutex_lock ( p->mutex );
1050 g_mutex_unlock ( p->mutex );
1053 /* Try to add file full_path.
1054 * full_path will be copied.
1055 * returns FALSE if file does not exists, TRUE otherwise.
1057 static gboolean dem_layer_add_file ( VikDEMLayer *vdl, const gchar *full_path )
1059 if ( g_file_test(full_path, G_FILE_TEST_EXISTS ) == TRUE ) {
1060 /* only load if file size is not 0 (not in progress) */
1062 stat (full_path, &sb);
1064 gchar *duped_path = g_strdup(full_path);
1065 vdl->files = g_list_prepend ( vdl->files, duped_path );
1066 a_dems_load ( duped_path );
1067 g_debug("%s: %s", __FUNCTION__, duped_path);
1068 vik_layer_emit_update ( VIK_LAYER(vdl) );
1075 static void dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
1077 if ( p->source == DEM_SOURCE_SRTM )
1078 srtm_dem_download_thread ( p, threaddata );
1079 #ifdef VIK_CONFIG_DEM24K
1080 else if ( p->source == DEM_SOURCE_DEM24K )
1081 dem24k_dem_download_thread ( p, threaddata );
1084 gdk_threads_enter();
1085 g_mutex_lock ( p->mutex );
1087 g_object_weak_unref ( G_OBJECT(p->vdl), weak_ref_cb, p );
1089 if ( dem_layer_add_file ( p->vdl, p->dest ) )
1090 vik_layer_emit_update ( VIK_LAYER(p->vdl) );
1092 g_mutex_unlock ( p->mutex );
1093 gdk_threads_leave();
1097 static void free_dem_download_params ( DEMDownloadParams *p )
1099 g_mutex_free ( p->mutex );
1104 static gpointer dem_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1110 static gboolean dem_layer_download_release ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1116 gchar *dem_file = NULL;
1118 if ( vdl->source == DEM_SOURCE_NONE )
1119 a_dialog_error_msg ( VIK_GTK_WINDOW_FROM_LAYER(vdl), _("No download source selected. Edit layer properties.") );
1121 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1122 vik_coord_to_latlon ( &coord, &ll );
1125 if ( vdl->source == DEM_SOURCE_SRTM )
1126 dem_file = srtm_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1127 #ifdef VIK_CONFIG_DEM24K
1128 else if ( vdl->source == DEM_SOURCE_DEM24K )
1129 dem_file = dem24k_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1135 full_path = g_strdup_printf("%s%s", MAPS_CACHE_DIR, dem_file );
1137 g_debug("%s: %s", __FUNCTION__, full_path);
1139 // TODO: check if already in filelist
1141 if ( ! dem_layer_add_file(vdl, full_path) ) {
1142 gchar *tmp = g_strdup_printf ( _("Downloading DEM %s"), dem_file );
1143 DEMDownloadParams *p = g_malloc(sizeof(DEMDownloadParams));
1144 p->dest = g_strdup(full_path);
1148 p->mutex = g_mutex_new();
1149 p->source = vdl->source;
1150 g_object_weak_ref(G_OBJECT(p->vdl), weak_ref_cb, p );
1152 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vdl), tmp,
1153 (vik_thr_func) dem_download_thread, p,
1154 (vik_thr_free_func) free_dem_download_params, NULL, 1 );
1159 g_free ( dem_file );
1160 g_free ( full_path );
1165 static gboolean dem_layer_download_click ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1167 /* choose & keep track of cache dir
1168 * download in background thread
1169 * download over area */