2 * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
4 * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
28 #ifdef HAVE_SYS_TYPES_H
29 #include <sys/types.h>
31 #ifdef HAVE_SYS_STAT_H
38 #include <glib/gi18n.h>
45 #include "background.h"
46 #include "vikwaypoint.h"
48 #include "vikviewport.h"
49 #include "viktreeview.h"
51 #include "vikaggregatelayer.h"
52 #include "viklayerspanel.h"
53 #include "vikmapslayer.h"
54 #include "vikdemlayer.h"
60 #include "icons/icons.h"
62 #define MAPS_CACHE_DIR maps_layer_default_dir()
64 #define SRTM_CACHE_TEMPLATE "%ssrtm3-%s%s%c%02d%c%03d.hgt.zip"
65 #define SRTM_HTTP_SITE "dds.cr.usgs.gov"
66 #define SRTM_HTTP_URI "/srtm/version2_1/SRTM3/"
68 #ifdef VIK_CONFIG_DEM24K
69 #define DEM24K_DOWNLOAD_SCRIPT "dem24k.pl"
72 #define UNUSED_LINE_THICKNESS 3
74 static const gchar* dem_layer_tooltip( VikDEMLayer *vdl );
75 static void dem_layer_marshall( VikDEMLayer *vdl, guint8 **data, gint *len );
76 static VikDEMLayer *dem_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
77 static gboolean dem_layer_set_param ( VikDEMLayer *vdl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
78 static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gboolean is_file_operation );
79 static void dem_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file );
80 static void srtm_draw_existence ( VikViewport *vp );
82 #ifdef VIK_CONFIG_DEM24K
83 static void dem24k_draw_existence ( VikViewport *vp );
86 /* Upped upper limit incase units are feet */
87 static VikLayerParamScale param_scales[] = {
92 static gchar *params_source[] = {
93 "SRTM Global 90m (3 arcsec)",
94 #ifdef VIK_CONFIG_DEM24K
100 static gchar *params_type[] = {
101 N_("Absolute height"),
102 N_("Height gradient"),
106 enum { DEM_SOURCE_SRTM,
107 #ifdef VIK_CONFIG_DEM24K
112 enum { DEM_TYPE_HEIGHT = 0,
117 static VikLayerParam dem_layer_params[] = {
118 { "files", VIK_LAYER_PARAM_STRING_LIST, VIK_LAYER_GROUP_NONE, N_("DEM Files:"), VIK_LAYER_WIDGET_FILELIST },
119 { "source", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Download Source:"), VIK_LAYER_WIDGET_RADIOGROUP_STATIC, params_source, NULL },
120 { "color", VIK_LAYER_PARAM_COLOR, VIK_LAYER_GROUP_NONE, N_("Min Elev Color:"), VIK_LAYER_WIDGET_COLOR, 0 },
121 { "type", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Type:"), VIK_LAYER_WIDGET_RADIOGROUP_STATIC, params_type, NULL },
122 { "min_elev", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_GROUP_NONE, N_("Min Elev:"), VIK_LAYER_WIDGET_SPINBUTTON, param_scales + 0 },
123 { "max_elev", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_GROUP_NONE, N_("Max Elev:"), VIK_LAYER_WIDGET_SPINBUTTON, param_scales + 0 },
127 enum { PARAM_FILES=0, PARAM_SOURCE, PARAM_COLOR, PARAM_TYPE, PARAM_MIN_ELEV, PARAM_MAX_ELEV, NUM_PARAMS };
129 static gpointer dem_layer_download_create ( VikWindow *vw, VikViewport *vvp);
130 static gboolean dem_layer_download_release ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp );
131 static gboolean dem_layer_download_click ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp );
133 static VikToolInterface dem_tools[] = {
134 { { "DEMDownload", "vik-icon-DEM Download", N_("_DEM Download"), NULL, N_("DEM Download"), 0 },
135 (VikToolConstructorFunc) dem_layer_download_create, NULL, NULL, NULL,
136 (VikToolMouseFunc) dem_layer_download_click, NULL, (VikToolMouseFunc) dem_layer_download_release,
137 (VikToolKeyFunc) NULL,
139 GDK_CURSOR_IS_PIXMAP, &cursor_demdl_pixbuf },
144 The first entry is blue for a default 'sea' colour,
145 however the value used by the corresponding gc can be configured as part of the DEM layer properties.
146 The other colours, shaded from brown to white are used to give an indication of height.
148 static gchar *dem_height_colors[] = {
150 "#9b793c", "#9c7d40", "#9d8144", "#9e8549", "#9f894d", "#a08d51", "#a29156", "#a3955a", "#a4995e", "#a69d63",
151 "#a89f65", "#aaa267", "#ada569", "#afa76b", "#b1aa6d", "#b4ad6f", "#b6b071", "#b9b373", "#bcb676", "#beb978",
152 "#c0bc7a", "#c2c07d", "#c4c37f", "#c6c681", "#c8ca84", "#cacd86", "#ccd188", "#cfd58b", "#c2ce84", "#b5c87e",
153 "#a9c278", "#9cbb71", "#8fb56b", "#83af65", "#76a95e", "#6aa358", "#5e9d52", "#63a055", "#69a458", "#6fa85c",
154 "#74ac5f", "#7ab063", "#80b467", "#86b86a", "#8cbc6e", "#92c072", "#94c175", "#97c278", "#9ac47c", "#9cc57f",
155 "#9fc682", "#a2c886", "#a4c989", "#a7cb8d", "#aacd91", "#afce99", "#b5d0a1", "#bbd2aa", "#c0d3b2", "#c6d5ba",
156 "#ccd7c3", "#d1d9cb", "#d7dbd4", "#DDDDDD", "#e0e0e0", "#e4e4e4", "#e8e8e8", "#ebebeb", "#efefef", "#f3f3f3",
157 "#f7f7f7", "#fbfbfb", "#ffffff"
160 static const guint DEM_N_HEIGHT_COLORS = sizeof(dem_height_colors)/sizeof(dem_height_colors[0]);
163 "#9b793c", "#9e8549", "#a29156", "#a69d63", "#ada569", "#b4ad6f", "#bcb676", "#c2c07d", "#c8ca84", "#cfd58b",
164 "#a9c278", "#83af65", "#5e9d52", "#6fa85c", "#80b467", "#92c072", "#9ac47c", "#a2c886", "#aacd91", "#bbd2aa",
165 "#ccd7c3", "#DDDDDD", "#e8e8e8", "#f3f3f3", "#FFFFFF"
169 static gchar *dem_gradient_colors[] = {
171 "#000000", "#000011", "#000022", "#000033", "#000044", "#00004c", "#000055", "#00005d", "#000066", "#00006e",
172 "#000077", "#00007f", "#000088", "#000090", "#000099", "#0000a1", "#0000aa", "#0000b2", "#0000bb", "#0000c3",
173 "#0000cc", "#0000d4", "#0000dd", "#0000e5", "#0000ee", "#0000f6", "#0000ff", "#0008f7", "#0011ee", "#0019e6",
174 "#0022dd", "#002ad5", "#0033cc", "#003bc4", "#0044bb", "#004cb3", "#0055aa", "#005da2", "#006699", "#006e91",
175 "#007788", "#007f80", "#008877", "#00906f", "#009966", "#00a15e", "#00aa55", "#00b24d", "#00bb44", "#00c33c",
176 "#00cc33", "#00d42b", "#00dd22", "#00e51a", "#00ee11", "#00f609", "#00ff00", "#08f700", "#11ee00", "#19e600",
177 "#22dd00", "#2ad500", "#33cc00", "#3bc400", "#44bb00", "#4cb300", "#55aa00", "#5da200", "#669900", "#6e9100",
178 "#778800", "#7f8000", "#887700", "#906f00", "#996600", "#a15e00", "#aa5500", "#b24d00", "#bb4400", "#c33c00",
179 "#cc3300", "#d42b00", "#dd2200", "#e51a00", "#ee1100", "#f60900", "#ff0000",
183 static const guint DEM_N_GRADIENT_COLORS = sizeof(dem_gradient_colors)/sizeof(dem_gradient_colors[0]);
186 VikLayerInterface vik_dem_layer_interface = {
192 sizeof(dem_tools) / sizeof(dem_tools[0]),
201 (VikLayerFuncCreate) vik_dem_layer_create,
202 (VikLayerFuncRealize) NULL,
203 (VikLayerFuncPostRead) dem_layer_post_read,
204 (VikLayerFuncFree) vik_dem_layer_free,
206 (VikLayerFuncProperties) NULL,
207 (VikLayerFuncDraw) vik_dem_layer_draw,
208 (VikLayerFuncChangeCoordMode) NULL,
210 (VikLayerFuncSetMenuItemsSelection) NULL,
211 (VikLayerFuncGetMenuItemsSelection) NULL,
213 (VikLayerFuncAddMenuItems) NULL,
214 (VikLayerFuncSublayerAddMenuItems) NULL,
216 (VikLayerFuncSublayerRenameRequest) NULL,
217 (VikLayerFuncSublayerToggleVisible) NULL,
218 (VikLayerFuncSublayerTooltip) NULL,
219 (VikLayerFuncLayerTooltip) dem_layer_tooltip,
220 (VikLayerFuncLayerSelected) NULL,
222 (VikLayerFuncMarshall) dem_layer_marshall,
223 (VikLayerFuncUnmarshall) dem_layer_unmarshall,
225 (VikLayerFuncSetParam) dem_layer_set_param,
226 (VikLayerFuncGetParam) dem_layer_get_param,
228 (VikLayerFuncReadFileData) NULL,
229 (VikLayerFuncWriteFileData) NULL,
231 (VikLayerFuncDeleteItem) NULL,
232 (VikLayerFuncCutItem) NULL,
233 (VikLayerFuncCopyItem) NULL,
234 (VikLayerFuncPasteItem) NULL,
235 (VikLayerFuncFreeCopiedItem) NULL,
236 (VikLayerFuncDragDropRequest) NULL,
238 (VikLayerFuncSelectClick) NULL,
239 (VikLayerFuncSelectMove) NULL,
240 (VikLayerFuncSelectRelease) NULL,
241 (VikLayerFuncSelectedViewportMenu) NULL,
244 struct _VikDEMLayer {
256 GType vik_dem_layer_get_type ()
258 static GType vdl_type = 0;
262 static const GTypeInfo vdl_info =
264 sizeof (VikDEMLayerClass),
265 NULL, /* base_init */
266 NULL, /* base_finalize */
267 NULL, /* class init */
268 NULL, /* class_finalize */
269 NULL, /* class_data */
270 sizeof (VikDEMLayer),
272 NULL /* instance init */
274 vdl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikDEMLayer", &vdl_info, 0 );
280 static const gchar* dem_layer_tooltip( VikDEMLayer *vdl )
282 static gchar tmp_buf[100];
283 g_snprintf (tmp_buf, sizeof(tmp_buf), _("Number of files: %d"), g_list_length (vdl->files));
287 static void dem_layer_marshall( VikDEMLayer *vdl, guint8 **data, gint *len )
289 vik_layer_marshall_params ( VIK_LAYER(vdl), data, len );
292 static VikDEMLayer *dem_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
294 VikDEMLayer *rv = vik_dem_layer_new ();
297 /* TODO: share GCS between layers */
298 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
299 rv->gcs[i] = vik_viewport_new_gc ( vvp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
301 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
302 rv->gcsgradient[i] = vik_viewport_new_gc ( vvp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
304 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
308 /* Structure for DEM data used in background thread */
311 } dem_load_thread_data;
314 * Function for starting the DEM file loading as a background thread
316 static int dem_layer_load_list_thread ( dem_load_thread_data *dltd, gpointer threaddata )
318 int result = 0; // Default to good
320 if ( a_dems_load_list ( &(dltd->vdl->files), threaddata ) ) {
325 // ATM as each file is processed the screen is not updated (no mechanism exposed to a_dems_load_list)
326 // Thus force draw only at the end, as loading is complete/aborted
327 //gdk_threads_enter();
328 // Test is helpful to prevent Gtk-CRITICAL warnings if the program is exitted whilst loading
329 if ( IS_VIK_LAYER(dltd->vdl) )
330 vik_layer_emit_update ( VIK_LAYER(dltd->vdl), TRUE ); // Yes update from background thread
331 //gdk_threads_leave();
336 static void dem_layer_thread_data_free ( dem_load_thread_data *data )
342 static void dem_layer_thread_cancel ( dem_load_thread_data *data )
345 // Instead of freeing the list, leave it as partially processed
346 // Thus we can see/use what was done
349 gboolean dem_layer_set_param ( VikDEMLayer *vdl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
353 case PARAM_COLOR: gdk_gc_set_rgb_fg_color ( vdl->gcs[0], &(data.c) ); break;
354 case PARAM_SOURCE: vdl->source = data.u; break;
355 case PARAM_TYPE: vdl->type = data.u; break;
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->min_elev = VIK_FEET_TO_METERS(data.d);
362 vdl->min_elev = data.d;
365 /* Convert to store internally
366 NB file operation always in internal units (metres) */
367 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
368 vdl->max_elev = VIK_FEET_TO_METERS(data.d);
370 vdl->max_elev = data.d;
374 // Clear out old settings - if any commonalities with new settings they will have to be read again
375 a_dems_list_free ( vdl->files );
376 // Set file list so any other intermediate screen drawing updates will show currently loaded DEMs by the working thread
377 vdl->files = data.sl;
379 dem_load_thread_data *dltd = g_malloc ( sizeof(dem_load_thread_data) );
381 dltd->vdl->files = data.sl;
383 a_background_thread ( VIK_GTK_WINDOW_FROM_WIDGET(vp),
385 (vik_thr_func) dem_layer_load_list_thread,
387 (vik_thr_free_func) dem_layer_thread_data_free,
388 (vik_thr_free_func) dem_layer_thread_cancel,
389 g_list_length ( data.sl ) ); // Number of DEM files
396 static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gboolean is_file_operation )
398 VikLayerParamData rv;
401 case PARAM_FILES: rv.sl = vdl->files; break;
402 case PARAM_SOURCE: rv.u = vdl->source; break;
403 case PARAM_TYPE: rv.u = vdl->type; break;
404 case PARAM_COLOR: vik_gc_get_fg_color ( vdl->gcs[0], &(rv.c) ); break;
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->min_elev);
411 rv.d = vdl->min_elev;
414 /* Convert for display in desired units
415 NB file operation always in internal units (metres) */
416 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
417 rv.d = VIK_METERS_TO_FEET(vdl->max_elev);
419 rv.d = vdl->max_elev;
425 static void dem_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file )
427 /* nothing ATM, but keep in case it's needed the future */
430 VikDEMLayer *vik_dem_layer_new ( )
432 VikDEMLayer *vdl = VIK_DEM_LAYER ( g_object_new ( VIK_DEM_LAYER_TYPE, NULL ) );
434 vik_layer_init ( VIK_LAYER(vdl), VIK_LAYER_DEM );
438 vdl->gcs = g_malloc(sizeof(GdkGC *)*DEM_N_HEIGHT_COLORS);
439 vdl->gcsgradient = g_malloc(sizeof(GdkGC *)*DEM_N_GRADIENT_COLORS);
440 /* make new gcs only if we need it (copy layer -> use old) */
443 vdl->max_elev = 1000.0;
444 vdl->source = DEM_SOURCE_SRTM;
445 vdl->type = DEM_TYPE_HEIGHT;
450 static inline guint16 get_height_difference(gint16 elev, gint16 new_elev)
452 if(new_elev == VIK_DEM_INVALID_ELEVATION)
455 return abs(new_elev - elev);
459 static void vik_dem_layer_draw_dem ( VikDEMLayer *vdl, VikViewport *vp, VikDEM *dem )
461 VikDEMColumn *column, *prevcolumn, *nextcolumn;
463 struct LatLon dem_northeast, dem_southwest;
464 gdouble max_lat, max_lon, min_lat, min_lon;
466 /**** Check if viewport and DEM data overlap ****/
468 /* get min, max lat/lon of viewport */
469 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
471 /* get min, max lat/lon of DEM data */
472 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
473 dem_northeast.lat = dem->max_north / 3600.0;
474 dem_northeast.lon = dem->max_east / 3600.0;
475 dem_southwest.lat = dem->min_north / 3600.0;
476 dem_southwest.lon = dem->min_east / 3600.0;
477 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
478 struct UTM dem_northeast_utm, dem_southwest_utm;
479 dem_northeast_utm.northing = dem->max_north;
480 dem_northeast_utm.easting = dem->max_east;
481 dem_southwest_utm.northing = dem->min_north;
482 dem_southwest_utm.easting = dem->min_east;
483 dem_northeast_utm.zone = dem_southwest_utm.zone = dem->utm_zone;
484 dem_northeast_utm.letter = dem_southwest_utm.letter = dem->utm_letter;
486 a_coords_utm_to_latlon(&dem_northeast_utm, &dem_northeast);
487 a_coords_utm_to_latlon(&dem_southwest_utm, &dem_southwest);
490 if ( (max_lat > dem_northeast.lat && min_lat > dem_northeast.lat) ||
491 (max_lat < dem_southwest.lat && min_lat < dem_southwest.lat) )
493 else if ( (max_lon > dem_northeast.lon && min_lon > dem_northeast.lon) ||
494 (max_lon < dem_southwest.lon && min_lon < dem_southwest.lon) )
496 /* else they overlap */
498 /**** End Overlap Check ****/
499 /* boxes to show where we have DEM instead of actually drawing the DEM.
500 * useful if we want to see what areas we have coverage for (if we want
501 * to get elevation data for a track) but don't want to cover the map.
505 /* draw a box if a DEM is loaded. in future I'd like to add an option for this
506 * this is useful if we want to see what areas we have dem for but don't want to
507 * cover the map (or maybe we just need translucent DEM?) */
509 VikCoord demne, demsw;
511 vik_coord_load_from_latlon(&demne, vik_viewport_get_coord_mode(vp), &dem_northeast);
512 vik_coord_load_from_latlon(&demsw, vik_viewport_get_coord_mode(vp), &dem_southwest);
514 vik_viewport_coord_to_screen ( vp, &demne, &x1, &y1 );
515 vik_viewport_coord_to_screen ( vp, &demsw, &x2, &y2 );
517 if ( x1 > vik_viewport_get_width(vp) ) x1=vik_viewport_get_width(vp);
518 if ( y2 > vik_viewport_get_height(vp) ) y2=vik_viewport_get_height(vp);
519 if ( x2 < 0 ) x2 = 0;
520 if ( y1 < 0 ) y1 = 0;
521 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
522 FALSE, x2, y1, x1-x2, y2-y1 );
527 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
528 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
530 gdouble max_lat_as, max_lon_as, min_lat_as, min_lon_as;
531 gdouble start_lat_as, end_lat_as, start_lon_as, end_lon_as;
533 gdouble start_lat, end_lat, start_lon, end_lon;
535 struct LatLon counter;
537 guint x, y, start_x, start_y;
541 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 80 ); /* todo: smarter calculation. */
543 gdouble nscale_deg = dem->north_scale / ((gdouble) 3600);
544 gdouble escale_deg = dem->east_scale / ((gdouble) 3600);
546 max_lat_as = max_lat * 3600;
547 min_lat_as = min_lat * 3600;
548 max_lon_as = max_lon * 3600;
549 min_lon_as = min_lon * 3600;
551 start_lat_as = MAX(min_lat_as, dem->min_north);
552 end_lat_as = MIN(max_lat_as, dem->max_north);
553 start_lon_as = MAX(min_lon_as, dem->min_east);
554 end_lon_as = MIN(max_lon_as, dem->max_east);
556 start_lat = floor(start_lat_as / dem->north_scale) * nscale_deg;
557 end_lat = ceil (end_lat_as / dem->north_scale) * nscale_deg;
558 start_lon = floor(start_lon_as / dem->east_scale) * escale_deg;
559 end_lon = ceil (end_lon_as / dem->east_scale) * escale_deg;
561 vik_dem_east_north_to_xy ( dem, start_lon_as, start_lat_as, &start_x, &start_y );
562 guint gradient_skip_factor = 1;
563 if(vdl->type == DEM_TYPE_GRADIENT)
564 gradient_skip_factor = skip_factor;
566 /* verify sane elev interval */
567 if ( vdl->max_elev <= vdl->min_elev )
568 vdl->max_elev = vdl->min_elev + 1;
570 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 ) {
571 // NOTE: ( counter.lon <= end_lon + ESCALE_DEG*SKIP_FACTOR ) is neccessary so in high zoom modes,
572 // the leftmost column does also get drawn, if the center point is out of viewport.
573 if ( x >= 0 && x < dem->n_columns ) {
574 column = g_ptr_array_index ( dem->columns, x );
575 // get previous and next column. catch out-of-bound.
577 new_x -= gradient_skip_factor;
579 prevcolumn = g_ptr_array_index ( dem->columns, x+1);
581 prevcolumn = g_ptr_array_index ( dem->columns, new_x);
583 new_x += gradient_skip_factor;
584 if(new_x >= dem->n_columns)
585 nextcolumn = g_ptr_array_index ( dem->columns, x-1);
587 nextcolumn = g_ptr_array_index ( dem->columns, new_x);
589 for ( y=start_y, counter.lat = start_lat; counter.lat <= end_lat; counter.lat += nscale_deg * skip_factor, y += skip_factor ) {
590 if ( y > column->n_points )
593 elev = column->points[y];
595 // calculate bounding box for drawing
596 gint box_x, box_y, box_width, box_height;
599 box_c.lat += (nscale_deg * skip_factor)/2;
600 box_c.lon -= (escale_deg * skip_factor)/2;
601 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
602 vik_viewport_coord_to_screen(vp, &tmp, &box_x, &box_y);
603 // catch box at borders
608 box_c.lat -= nscale_deg * skip_factor;
609 box_c.lon += escale_deg * skip_factor;
610 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
611 vik_viewport_coord_to_screen(vp, &tmp, &box_width, &box_height);
614 // catch box at borders
615 if(box_width < 0 || box_height < 0)
616 continue; // skip this. this is out of our viewport anyway. FIXME: why?
618 gboolean below_minimum = FALSE;
619 if(vdl->type == DEM_TYPE_HEIGHT) {
620 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev ) {
621 // Prevent 'elev - vdl->min_elev' from being negative so can safely use as array index
622 elev = ceil ( vdl->min_elev );
623 below_minimum = TRUE;
625 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
626 elev = vdl->max_elev;
630 if(box_width < 0 || box_height < 0) // FIXME: why does this happen?
633 if(vdl->type == DEM_TYPE_GRADIENT) {
634 if( elev == VIK_DEM_INVALID_ELEVATION ) {
637 // calculate and sum gradient in all directions
641 // calculate gradient from height points all around the current one
642 new_y = y - gradient_skip_factor;
645 change += get_height_difference(elev, prevcolumn->points[new_y]);
646 change += get_height_difference(elev, column->points[new_y]);
647 change += get_height_difference(elev, nextcolumn->points[new_y]);
649 change += get_height_difference(elev, prevcolumn->points[y]);
650 change += get_height_difference(elev, nextcolumn->points[y]);
652 new_y = y + gradient_skip_factor;
653 if(new_y >= column->n_points)
655 change += get_height_difference(elev, prevcolumn->points[new_y]);
656 change += get_height_difference(elev, column->points[new_y]);
657 change += get_height_difference(elev, nextcolumn->points[new_y]);
659 change = change / ((skip_factor > 1) ? log(skip_factor) : 0.55); // FIXME: better calc.
661 if(change < vdl->min_elev)
662 // Prevent 'change - vdl->min_elev' from being negative so can safely use as array index
663 change = ceil ( vdl->min_elev );
665 if(change > vdl->max_elev)
666 change = vdl->max_elev;
668 // void vik_viewport_draw_rectangle ( VikViewport *vvp, GdkGC *gc, gboolean filled, gint x1, gint y1, gint x2, gint y2 );
669 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);
672 if(vdl->type == DEM_TYPE_HEIGHT) {
673 if ( elev == VIK_DEM_INVALID_ELEVATION )
674 ; /* don't draw it */
675 else if ( elev <= 0 || below_minimum )
676 /* If 'sea' colour or below the defined mininum draw in the configurable colour */
677 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, box_x, box_y, box_width, box_height);
679 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);
686 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
687 gdouble max_nor, max_eas, min_nor, min_eas;
688 gdouble start_nor, start_eas, end_nor, end_eas;
692 guint x, y, start_x, start_y;
694 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
697 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 10 ); /* todo: smarter calculation. */
699 VikCoord tleft, tright, bleft, bright;
701 vik_viewport_screen_to_coord ( vp, 0, 0, &tleft );
702 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), 0, &tright );
703 vik_viewport_screen_to_coord ( vp, 0, vik_viewport_get_height(vp), &bleft );
704 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), vik_viewport_get_height(vp), &bright );
707 vik_coord_convert(&tleft, VIK_COORD_UTM);
708 vik_coord_convert(&tright, VIK_COORD_UTM);
709 vik_coord_convert(&bleft, VIK_COORD_UTM);
710 vik_coord_convert(&bright, VIK_COORD_UTM);
712 max_nor = MAX(tleft.north_south, tright.north_south);
713 min_nor = MIN(bleft.north_south, bright.north_south);
714 max_eas = MAX(bright.east_west, tright.east_west);
715 min_eas = MIN(bleft.east_west, tleft.east_west);
717 start_nor = MAX(min_nor, dem->min_north);
718 end_nor = MIN(max_nor, dem->max_north);
719 if ( tleft.utm_zone == dem->utm_zone && bleft.utm_zone == dem->utm_zone
720 && (tleft.utm_letter >= 'N') == (dem->utm_letter >= 'N')
721 && (bleft.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
722 start_eas = MAX(min_eas, dem->min_east);
724 start_eas = dem->min_east;
725 if ( tright.utm_zone == dem->utm_zone && bright.utm_zone == dem->utm_zone
726 && (tright.utm_letter >= 'N') == (dem->utm_letter >= 'N')
727 && (bright.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
728 end_eas = MIN(max_eas, dem->max_east);
730 end_eas = dem->max_east;
732 start_nor = floor(start_nor / dem->north_scale) * dem->north_scale;
733 end_nor = ceil (end_nor / dem->north_scale) * dem->north_scale;
734 start_eas = floor(start_eas / dem->east_scale) * dem->east_scale;
735 end_eas = ceil (end_eas / dem->east_scale) * dem->east_scale;
737 vik_dem_east_north_to_xy ( dem, start_eas, start_nor, &start_x, &start_y );
739 /* TODO: why start_x and start_y are -1 -- rounding error from above? */
741 counter.zone = dem->utm_zone;
742 counter.letter = dem->utm_letter;
744 for ( x=start_x, counter.easting = start_eas; counter.easting <= end_eas; counter.easting += dem->east_scale * skip_factor, x += skip_factor ) {
745 if ( x > 0 && x < dem->n_columns ) {
746 column = g_ptr_array_index ( dem->columns, x );
747 for ( y=start_y, counter.northing = start_nor; counter.northing <= end_nor; counter.northing += dem->north_scale * skip_factor, y += skip_factor ) {
748 if ( y > column->n_points )
750 elev = column->points[y];
751 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev )
753 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
758 vik_coord_load_from_utm(&tmp, vik_viewport_get_coord_mode(vp), &counter);
759 vik_viewport_coord_to_screen(vp, &tmp, &a, &b);
760 if ( elev == VIK_DEM_INVALID_ELEVATION )
761 ; /* don't draw it */
762 else if ( elev <= 0 )
763 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, a-1, b-1, 2, 2 );
765 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 );
773 /* return the continent for the specified lat, lon */
775 static const gchar *srtm_continent_dir ( gint lat, gint lon )
777 extern const char *_srtm_continent_data[];
778 static GHashTable *srtm_continent = NULL;
779 const gchar *continent;
782 if (!srtm_continent) {
785 srtm_continent = g_hash_table_new(g_str_hash, g_str_equal);
786 s = _srtm_continent_data;
787 while (*s != (gchar *)-1) {
790 g_hash_table_insert(srtm_continent, (gpointer) *s, (gpointer) continent);
796 g_snprintf(name, sizeof(name), "%c%02d%c%03d",
797 (lat >= 0) ? 'N' : 'S', ABS(lat),
798 (lon >= 0) ? 'E' : 'W', ABS(lon));
800 return(g_hash_table_lookup(srtm_continent, name));
803 void vik_dem_layer_draw ( VikDEMLayer *vdl, gpointer data )
805 VikViewport *vp = (VikViewport *) data;
806 GList *dems_iter = vdl->files;
810 /* search for SRTM3 90m */
812 if ( vdl->source == DEM_SOURCE_SRTM )
813 srtm_draw_existence ( vp );
814 #ifdef VIK_CONFIG_DEM24K
815 else if ( vdl->source == DEM_SOURCE_DEM24K )
816 dem24k_draw_existence ( vp );
819 while ( dems_iter ) {
820 dem = a_dems_get ( (const char *) (dems_iter->data) );
822 vik_dem_layer_draw_dem ( vdl, vp, dem );
823 dems_iter = dems_iter->next;
827 void vik_dem_layer_free ( VikDEMLayer *vdl )
830 if ( vdl->color != NULL )
831 g_object_unref ( vdl->color );
834 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
835 g_object_unref ( vdl->gcs[i] );
838 if ( vdl->gcsgradient )
839 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
840 g_object_unref ( vdl->gcsgradient[i] );
841 g_free ( vdl->gcsgradient );
843 a_dems_list_free ( vdl->files );
846 VikDEMLayer *vik_dem_layer_create ( VikViewport *vp )
848 VikDEMLayer *vdl = vik_dem_layer_new ();
851 /* TODO: share GCS between layers */
852 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
853 vdl->gcs[i] = vik_viewport_new_gc ( vp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
855 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
856 vdl->gcsgradient[i] = vik_viewport_new_gc ( vp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
860 /**************************************************************
861 **** SOURCES & DOWNLOADING
862 **************************************************************/
868 VikDEMLayer *vdl; /* NULL if not alive */
874 /**************************************************
876 **************************************************/
878 static void srtm_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
881 const gchar *continent_dir;
883 intlat = (int)floor(p->lat);
884 intlon = (int)floor(p->lon);
885 continent_dir = srtm_continent_dir(intlat, intlon);
887 if (!continent_dir) {
888 g_warning(N_("No SRTM data available for %f, %f"), p->lat, p->lon);
892 gchar *src_fn = g_strdup_printf("%s%s/%c%02d%c%03d.hgt.zip",
895 (intlat >= 0) ? 'N' : 'S',
897 (intlon >= 0) ? 'E' : 'W',
900 static DownloadMapOptions options = { FALSE, FALSE, NULL, 0, a_check_map_file };
901 a_http_download_get_url ( SRTM_HTTP_SITE, src_fn, p->dest, &options, NULL );
905 static gchar *srtm_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
908 const gchar *continent_dir;
910 intlat = (int)floor(lat);
911 intlon = (int)floor(lon);
912 continent_dir = srtm_continent_dir(intlat, intlon);
915 continent_dir = "nowhere";
917 return g_strdup_printf("srtm3-%s%s%c%02d%c%03d.hgt.zip",
920 (intlat >= 0) ? 'N' : 'S',
922 (intlon >= 0) ? 'E' : 'W',
927 /* TODO: generalize */
928 static void srtm_draw_existence ( VikViewport *vp )
930 gdouble max_lat, max_lon, min_lat, min_lon;
931 gchar buf[strlen(MAPS_CACHE_DIR)+strlen(SRTM_CACHE_TEMPLATE)+30];
934 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
936 for (i = floor(min_lat); i <= floor(max_lat); i++) {
937 for (j = floor(min_lon); j <= floor(max_lon); j++) {
938 const gchar *continent_dir;
939 if ((continent_dir = srtm_continent_dir(i, j)) == NULL)
941 g_snprintf(buf, sizeof(buf), SRTM_CACHE_TEMPLATE,
945 (i >= 0) ? 'N' : 'S',
947 (j >= 0) ? 'E' : 'W',
949 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
954 sw.mode = VIK_COORD_LATLON;
955 ne.north_south = i+1;
957 ne.mode = VIK_COORD_LATLON;
958 vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
959 vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
960 if ( x1 < 0 ) x1 = 0;
961 if ( y2 < 0 ) y2 = 0;
962 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
963 FALSE, x1, y2, x2-x1, y1-y2 );
970 /**************************************************
972 **************************************************/
974 #ifdef VIK_CONFIG_DEM24K
976 static void dem24k_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
979 gchar *cmdline = g_strdup_printf("%s %.03f %.03f",
980 DEM24K_DOWNLOAD_SCRIPT,
983 /* FIX: don't use system, use execv or something. check for existence */
988 static gchar *dem24k_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
990 return g_strdup_printf("dem24k/%d/%d/%.03f,%.03f.dem",
997 /* TODO: generalize */
998 static void dem24k_draw_existence ( VikViewport *vp )
1000 gdouble max_lat, max_lon, min_lat, min_lon;
1001 gchar buf[strlen(MAPS_CACHE_DIR)+40];
1004 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
1006 for (i = floor(min_lat*8)/8; i <= floor(max_lat*8)/8; i+=0.125) {
1007 /* check lat dir first -- faster */
1008 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/",
1011 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1013 for (j = floor(min_lon*8)/8; j <= floor(max_lon*8)/8; j+=0.125) {
1014 /* check lon dir first -- faster */
1015 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/",
1019 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1021 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/%.03f,%.03f.dem",
1027 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1029 gint x1, y1, x2, y2;
1031 sw.east_west = j-0.125;
1032 sw.mode = VIK_COORD_LATLON;
1033 ne.north_south = i+0.125;
1035 ne.mode = VIK_COORD_LATLON;
1036 vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
1037 vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
1038 if ( x1 < 0 ) x1 = 0;
1039 if ( y2 < 0 ) y2 = 0;
1040 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
1041 FALSE, x1, y2, x2-x1, y1-y2 );
1048 /**************************************************
1049 * SOURCES -- DOWNLOADING & IMPORTING TOOL *
1050 **************************************************
1053 static void weak_ref_cb ( gpointer ptr, GObject * dead_vdl )
1055 DEMDownloadParams *p = ptr;
1056 g_mutex_lock ( p->mutex );
1058 g_mutex_unlock ( p->mutex );
1061 /* Try to add file full_path.
1062 * full_path will be copied.
1063 * returns FALSE if file does not exists, TRUE otherwise.
1065 static gboolean dem_layer_add_file ( VikDEMLayer *vdl, const gchar *full_path )
1067 if ( g_file_test(full_path, G_FILE_TEST_EXISTS ) == TRUE ) {
1068 /* only load if file size is not 0 (not in progress) */
1070 stat (full_path, &sb);
1072 gchar *duped_path = g_strdup(full_path);
1073 vdl->files = g_list_prepend ( vdl->files, duped_path );
1074 a_dems_load ( duped_path );
1075 g_debug("%s: %s", __FUNCTION__, duped_path);
1082 static void dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
1084 if ( p->source == DEM_SOURCE_SRTM )
1085 srtm_dem_download_thread ( p, threaddata );
1086 #ifdef VIK_CONFIG_DEM24K
1087 else if ( p->source == DEM_SOURCE_DEM24K )
1088 dem24k_dem_download_thread ( p, threaddata );
1093 //gdk_threads_enter();
1094 g_mutex_lock ( p->mutex );
1096 g_object_weak_unref ( G_OBJECT(p->vdl), weak_ref_cb, p );
1098 if ( dem_layer_add_file ( p->vdl, p->dest ) )
1099 vik_layer_emit_update ( VIK_LAYER(p->vdl), TRUE ); // Yes update from background thread
1101 g_mutex_unlock ( p->mutex );
1102 //gdk_threads_leave();
1106 static void free_dem_download_params ( DEMDownloadParams *p )
1108 g_mutex_free ( p->mutex );
1113 static gpointer dem_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1119 static gboolean dem_layer_download_release ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1125 gchar *dem_file = NULL;
1127 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1128 vik_coord_to_latlon ( &coord, &ll );
1131 if ( vdl->source == DEM_SOURCE_SRTM )
1132 dem_file = srtm_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1133 #ifdef VIK_CONFIG_DEM24K
1134 else if ( vdl->source == DEM_SOURCE_DEM24K )
1135 dem_file = dem24k_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1141 full_path = g_strdup_printf("%s%s", MAPS_CACHE_DIR, dem_file );
1143 g_debug("%s: %s", __FUNCTION__, full_path);
1145 // TODO: check if already in filelist
1147 if ( ! dem_layer_add_file(vdl, full_path) ) {
1148 gchar *tmp = g_strdup_printf ( _("Downloading DEM %s"), dem_file );
1149 DEMDownloadParams *p = g_malloc(sizeof(DEMDownloadParams));
1150 p->dest = g_strdup(full_path);
1154 p->mutex = g_mutex_new();
1155 p->source = vdl->source;
1156 g_object_weak_ref(G_OBJECT(p->vdl), weak_ref_cb, p );
1158 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vdl), tmp,
1159 (vik_thr_func) dem_download_thread, p,
1160 (vik_thr_free_func) free_dem_download_params, NULL, 1 );
1165 vik_layer_emit_update ( VIK_LAYER(vdl), FALSE );
1167 g_free ( dem_file );
1168 g_free ( full_path );
1173 static gboolean dem_layer_download_click ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1175 /* choose & keep track of cache dir
1176 * download in background thread
1177 * download over area */