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, NULL, NULL, NULL },
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, NULL, NULL, NULL },
121 { "type", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Type:"), VIK_LAYER_WIDGET_RADIOGROUP_STATIC, params_type, NULL, NULL },
122 { "min_elev", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_GROUP_NONE, N_("Min Elev:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶m_scales[0], NULL, NULL },
123 { "max_elev", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_GROUP_NONE, N_("Max Elev:"), VIK_LAYER_WIDGET_SPINBUTTON, ¶m_scales[0], NULL, NULL },
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 = {
193 sizeof(dem_tools) / sizeof(dem_tools[0]),
202 (VikLayerFuncCreate) vik_dem_layer_create,
203 (VikLayerFuncRealize) NULL,
204 (VikLayerFuncPostRead) dem_layer_post_read,
205 (VikLayerFuncFree) vik_dem_layer_free,
207 (VikLayerFuncProperties) NULL,
208 (VikLayerFuncDraw) vik_dem_layer_draw,
209 (VikLayerFuncChangeCoordMode) NULL,
211 (VikLayerFuncSetMenuItemsSelection) NULL,
212 (VikLayerFuncGetMenuItemsSelection) NULL,
214 (VikLayerFuncAddMenuItems) NULL,
215 (VikLayerFuncSublayerAddMenuItems) NULL,
217 (VikLayerFuncSublayerRenameRequest) NULL,
218 (VikLayerFuncSublayerToggleVisible) NULL,
219 (VikLayerFuncSublayerTooltip) NULL,
220 (VikLayerFuncLayerTooltip) dem_layer_tooltip,
221 (VikLayerFuncLayerSelected) NULL,
223 (VikLayerFuncMarshall) dem_layer_marshall,
224 (VikLayerFuncUnmarshall) dem_layer_unmarshall,
226 (VikLayerFuncSetParam) dem_layer_set_param,
227 (VikLayerFuncGetParam) dem_layer_get_param,
229 (VikLayerFuncReadFileData) NULL,
230 (VikLayerFuncWriteFileData) NULL,
232 (VikLayerFuncDeleteItem) NULL,
233 (VikLayerFuncCutItem) NULL,
234 (VikLayerFuncCopyItem) NULL,
235 (VikLayerFuncPasteItem) NULL,
236 (VikLayerFuncFreeCopiedItem) NULL,
237 (VikLayerFuncDragDropRequest) NULL,
239 (VikLayerFuncSelectClick) NULL,
240 (VikLayerFuncSelectMove) NULL,
241 (VikLayerFuncSelectRelease) NULL,
242 (VikLayerFuncSelectedViewportMenu) NULL,
245 struct _VikDEMLayer {
257 GType vik_dem_layer_get_type ()
259 static GType vdl_type = 0;
263 static const GTypeInfo vdl_info =
265 sizeof (VikDEMLayerClass),
266 NULL, /* base_init */
267 NULL, /* base_finalize */
268 NULL, /* class init */
269 NULL, /* class_finalize */
270 NULL, /* class_data */
271 sizeof (VikDEMLayer),
273 NULL /* instance init */
275 vdl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikDEMLayer", &vdl_info, 0 );
281 static const gchar* dem_layer_tooltip( VikDEMLayer *vdl )
283 static gchar tmp_buf[100];
284 g_snprintf (tmp_buf, sizeof(tmp_buf), _("Number of files: %d"), g_list_length (vdl->files));
288 static void dem_layer_marshall( VikDEMLayer *vdl, guint8 **data, gint *len )
290 vik_layer_marshall_params ( VIK_LAYER(vdl), data, len );
293 static VikDEMLayer *dem_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
295 VikDEMLayer *rv = vik_dem_layer_new ();
298 /* TODO: share GCS between layers */
299 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
300 rv->gcs[i] = vik_viewport_new_gc ( vvp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
302 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
303 rv->gcsgradient[i] = vik_viewport_new_gc ( vvp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
305 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
309 /* Structure for DEM data used in background thread */
312 } dem_load_thread_data;
315 * Function for starting the DEM file loading as a background thread
317 static int dem_layer_load_list_thread ( dem_load_thread_data *dltd, gpointer threaddata )
319 int result = 0; // Default to good
321 if ( a_dems_load_list ( &(dltd->vdl->files), threaddata ) ) {
326 // ATM as each file is processed the screen is not updated (no mechanism exposed to a_dems_load_list)
327 // Thus force draw only at the end, as loading is complete/aborted
328 //gdk_threads_enter();
329 // Test is helpful to prevent Gtk-CRITICAL warnings if the program is exitted whilst loading
330 if ( IS_VIK_LAYER(dltd->vdl) )
331 vik_layer_emit_update ( VIK_LAYER(dltd->vdl), TRUE ); // Yes update from background thread
332 //gdk_threads_leave();
337 static void dem_layer_thread_data_free ( dem_load_thread_data *data )
343 static void dem_layer_thread_cancel ( dem_load_thread_data *data )
346 // Instead of freeing the list, leave it as partially processed
347 // Thus we can see/use what was done
350 gboolean dem_layer_set_param ( VikDEMLayer *vdl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
354 case PARAM_COLOR: gdk_gc_set_rgb_fg_color ( vdl->gcs[0], &(data.c) ); break;
355 case PARAM_SOURCE: vdl->source = data.u; break;
356 case PARAM_TYPE: vdl->type = data.u; break;
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->min_elev = VIK_FEET_TO_METERS(data.d);
363 vdl->min_elev = data.d;
366 /* Convert to store internally
367 NB file operation always in internal units (metres) */
368 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
369 vdl->max_elev = VIK_FEET_TO_METERS(data.d);
371 vdl->max_elev = data.d;
375 // Clear out old settings - if any commonalities with new settings they will have to be read again
376 a_dems_list_free ( vdl->files );
377 // Set file list so any other intermediate screen drawing updates will show currently loaded DEMs by the working thread
378 vdl->files = data.sl;
380 dem_load_thread_data *dltd = g_malloc ( sizeof(dem_load_thread_data) );
382 dltd->vdl->files = data.sl;
384 a_background_thread ( VIK_GTK_WINDOW_FROM_WIDGET(vp),
386 (vik_thr_func) dem_layer_load_list_thread,
388 (vik_thr_free_func) dem_layer_thread_data_free,
389 (vik_thr_free_func) dem_layer_thread_cancel,
390 g_list_length ( data.sl ) ); // Number of DEM files
397 static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gboolean is_file_operation )
399 VikLayerParamData rv;
402 case PARAM_FILES: rv.sl = vdl->files; break;
403 case PARAM_SOURCE: rv.u = vdl->source; break;
404 case PARAM_TYPE: rv.u = vdl->type; break;
405 case PARAM_COLOR: vik_gc_get_fg_color ( vdl->gcs[0], &(rv.c) ); break;
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->min_elev);
412 rv.d = vdl->min_elev;
415 /* Convert for display in desired units
416 NB file operation always in internal units (metres) */
417 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
418 rv.d = VIK_METERS_TO_FEET(vdl->max_elev);
420 rv.d = vdl->max_elev;
426 static void dem_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file )
428 /* nothing ATM, but keep in case it's needed the future */
431 VikDEMLayer *vik_dem_layer_new ( )
433 VikDEMLayer *vdl = VIK_DEM_LAYER ( g_object_new ( VIK_DEM_LAYER_TYPE, NULL ) );
435 vik_layer_set_type ( VIK_LAYER(vdl), VIK_LAYER_DEM );
439 vdl->gcs = g_malloc(sizeof(GdkGC *)*DEM_N_HEIGHT_COLORS);
440 vdl->gcsgradient = g_malloc(sizeof(GdkGC *)*DEM_N_GRADIENT_COLORS);
441 /* make new gcs only if we need it (copy layer -> use old) */
444 vdl->max_elev = 1000.0;
445 vdl->source = DEM_SOURCE_SRTM;
446 vdl->type = DEM_TYPE_HEIGHT;
451 static inline guint16 get_height_difference(gint16 elev, gint16 new_elev)
453 if(new_elev == VIK_DEM_INVALID_ELEVATION)
456 return abs(new_elev - elev);
460 static void vik_dem_layer_draw_dem ( VikDEMLayer *vdl, VikViewport *vp, VikDEM *dem )
462 VikDEMColumn *column, *prevcolumn, *nextcolumn;
464 struct LatLon dem_northeast, dem_southwest;
465 gdouble max_lat, max_lon, min_lat, min_lon;
467 /**** Check if viewport and DEM data overlap ****/
469 /* get min, max lat/lon of viewport */
470 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
472 /* get min, max lat/lon of DEM data */
473 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
474 dem_northeast.lat = dem->max_north / 3600.0;
475 dem_northeast.lon = dem->max_east / 3600.0;
476 dem_southwest.lat = dem->min_north / 3600.0;
477 dem_southwest.lon = dem->min_east / 3600.0;
478 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
479 struct UTM dem_northeast_utm, dem_southwest_utm;
480 dem_northeast_utm.northing = dem->max_north;
481 dem_northeast_utm.easting = dem->max_east;
482 dem_southwest_utm.northing = dem->min_north;
483 dem_southwest_utm.easting = dem->min_east;
484 dem_northeast_utm.zone = dem_southwest_utm.zone = dem->utm_zone;
485 dem_northeast_utm.letter = dem_southwest_utm.letter = dem->utm_letter;
487 a_coords_utm_to_latlon(&dem_northeast_utm, &dem_northeast);
488 a_coords_utm_to_latlon(&dem_southwest_utm, &dem_southwest);
491 if ( (max_lat > dem_northeast.lat && min_lat > dem_northeast.lat) ||
492 (max_lat < dem_southwest.lat && min_lat < dem_southwest.lat) )
494 else if ( (max_lon > dem_northeast.lon && min_lon > dem_northeast.lon) ||
495 (max_lon < dem_southwest.lon && min_lon < dem_southwest.lon) )
497 /* else they overlap */
499 /**** End Overlap Check ****/
500 /* boxes to show where we have DEM instead of actually drawing the DEM.
501 * useful if we want to see what areas we have coverage for (if we want
502 * to get elevation data for a track) but don't want to cover the map.
506 /* draw a box if a DEM is loaded. in future I'd like to add an option for this
507 * this is useful if we want to see what areas we have dem for but don't want to
508 * cover the map (or maybe we just need translucent DEM?) */
510 VikCoord demne, demsw;
512 vik_coord_load_from_latlon(&demne, vik_viewport_get_coord_mode(vp), &dem_northeast);
513 vik_coord_load_from_latlon(&demsw, vik_viewport_get_coord_mode(vp), &dem_southwest);
515 vik_viewport_coord_to_screen ( vp, &demne, &x1, &y1 );
516 vik_viewport_coord_to_screen ( vp, &demsw, &x2, &y2 );
518 if ( x1 > vik_viewport_get_width(vp) ) x1=vik_viewport_get_width(vp);
519 if ( y2 > vik_viewport_get_height(vp) ) y2=vik_viewport_get_height(vp);
520 if ( x2 < 0 ) x2 = 0;
521 if ( y1 < 0 ) y1 = 0;
522 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
523 FALSE, x2, y1, x1-x2, y2-y1 );
528 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
529 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
531 gdouble max_lat_as, max_lon_as, min_lat_as, min_lon_as;
532 gdouble start_lat_as, end_lat_as, start_lon_as, end_lon_as;
534 gdouble start_lat, end_lat, start_lon, end_lon;
536 struct LatLon counter;
538 guint x, y, start_x, start_y;
542 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 80 ); /* todo: smarter calculation. */
544 gdouble nscale_deg = dem->north_scale / ((gdouble) 3600);
545 gdouble escale_deg = dem->east_scale / ((gdouble) 3600);
547 max_lat_as = max_lat * 3600;
548 min_lat_as = min_lat * 3600;
549 max_lon_as = max_lon * 3600;
550 min_lon_as = min_lon * 3600;
552 start_lat_as = MAX(min_lat_as, dem->min_north);
553 end_lat_as = MIN(max_lat_as, dem->max_north);
554 start_lon_as = MAX(min_lon_as, dem->min_east);
555 end_lon_as = MIN(max_lon_as, dem->max_east);
557 start_lat = floor(start_lat_as / dem->north_scale) * nscale_deg;
558 end_lat = ceil (end_lat_as / dem->north_scale) * nscale_deg;
559 start_lon = floor(start_lon_as / dem->east_scale) * escale_deg;
560 end_lon = ceil (end_lon_as / dem->east_scale) * escale_deg;
562 vik_dem_east_north_to_xy ( dem, start_lon_as, start_lat_as, &start_x, &start_y );
563 guint gradient_skip_factor = 1;
564 if(vdl->type == DEM_TYPE_GRADIENT)
565 gradient_skip_factor = skip_factor;
567 /* verify sane elev interval */
568 if ( vdl->max_elev <= vdl->min_elev )
569 vdl->max_elev = vdl->min_elev + 1;
571 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 ) {
572 // NOTE: ( counter.lon <= end_lon + ESCALE_DEG*SKIP_FACTOR ) is neccessary so in high zoom modes,
573 // the leftmost column does also get drawn, if the center point is out of viewport.
574 if ( x >= 0 && x < dem->n_columns ) {
575 column = g_ptr_array_index ( dem->columns, x );
576 // get previous and next column. catch out-of-bound.
578 new_x -= gradient_skip_factor;
580 prevcolumn = g_ptr_array_index ( dem->columns, x+1);
582 prevcolumn = g_ptr_array_index ( dem->columns, new_x);
584 new_x += gradient_skip_factor;
585 if(new_x >= dem->n_columns)
586 nextcolumn = g_ptr_array_index ( dem->columns, x-1);
588 nextcolumn = g_ptr_array_index ( dem->columns, new_x);
590 for ( y=start_y, counter.lat = start_lat; counter.lat <= end_lat; counter.lat += nscale_deg * skip_factor, y += skip_factor ) {
591 if ( y > column->n_points )
594 elev = column->points[y];
596 // calculate bounding box for drawing
597 gint box_x, box_y, box_width, box_height;
600 box_c.lat += (nscale_deg * skip_factor)/2;
601 box_c.lon -= (escale_deg * skip_factor)/2;
602 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
603 vik_viewport_coord_to_screen(vp, &tmp, &box_x, &box_y);
604 // catch box at borders
609 box_c.lat -= nscale_deg * skip_factor;
610 box_c.lon += escale_deg * skip_factor;
611 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
612 vik_viewport_coord_to_screen(vp, &tmp, &box_width, &box_height);
615 // catch box at borders
616 if(box_width < 0 || box_height < 0)
617 continue; // skip this. this is out of our viewport anyway. FIXME: why?
619 gboolean below_minimum = FALSE;
620 if(vdl->type == DEM_TYPE_HEIGHT) {
621 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev ) {
622 // Prevent 'elev - vdl->min_elev' from being negative so can safely use as array index
623 elev = ceil ( vdl->min_elev );
624 below_minimum = TRUE;
626 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
627 elev = vdl->max_elev;
631 if(box_width < 0 || box_height < 0) // FIXME: why does this happen?
634 if(vdl->type == DEM_TYPE_GRADIENT) {
635 if( elev == VIK_DEM_INVALID_ELEVATION ) {
638 // calculate and sum gradient in all directions
642 // calculate gradient from height points all around the current one
643 new_y = y - gradient_skip_factor;
646 change += get_height_difference(elev, prevcolumn->points[new_y]);
647 change += get_height_difference(elev, column->points[new_y]);
648 change += get_height_difference(elev, nextcolumn->points[new_y]);
650 change += get_height_difference(elev, prevcolumn->points[y]);
651 change += get_height_difference(elev, nextcolumn->points[y]);
653 new_y = y + gradient_skip_factor;
654 if(new_y >= column->n_points)
656 change += get_height_difference(elev, prevcolumn->points[new_y]);
657 change += get_height_difference(elev, column->points[new_y]);
658 change += get_height_difference(elev, nextcolumn->points[new_y]);
660 change = change / ((skip_factor > 1) ? log(skip_factor) : 0.55); // FIXME: better calc.
662 if(change < vdl->min_elev)
663 // Prevent 'change - vdl->min_elev' from being negative so can safely use as array index
664 change = ceil ( vdl->min_elev );
666 if(change > vdl->max_elev)
667 change = vdl->max_elev;
669 // void vik_viewport_draw_rectangle ( VikViewport *vvp, GdkGC *gc, gboolean filled, gint x1, gint y1, gint x2, gint y2 );
670 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);
673 if(vdl->type == DEM_TYPE_HEIGHT) {
674 if ( elev == VIK_DEM_INVALID_ELEVATION )
675 ; /* don't draw it */
676 else if ( elev <= 0 || below_minimum )
677 /* If 'sea' colour or below the defined mininum draw in the configurable colour */
678 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, box_x, box_y, box_width, box_height);
680 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);
687 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
688 gdouble max_nor, max_eas, min_nor, min_eas;
689 gdouble start_nor, start_eas, end_nor, end_eas;
693 guint x, y, start_x, start_y;
695 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
698 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 10 ); /* todo: smarter calculation. */
700 VikCoord tleft, tright, bleft, bright;
702 vik_viewport_screen_to_coord ( vp, 0, 0, &tleft );
703 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), 0, &tright );
704 vik_viewport_screen_to_coord ( vp, 0, vik_viewport_get_height(vp), &bleft );
705 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), vik_viewport_get_height(vp), &bright );
708 vik_coord_convert(&tleft, VIK_COORD_UTM);
709 vik_coord_convert(&tright, VIK_COORD_UTM);
710 vik_coord_convert(&bleft, VIK_COORD_UTM);
711 vik_coord_convert(&bright, VIK_COORD_UTM);
713 max_nor = MAX(tleft.north_south, tright.north_south);
714 min_nor = MIN(bleft.north_south, bright.north_south);
715 max_eas = MAX(bright.east_west, tright.east_west);
716 min_eas = MIN(bleft.east_west, tleft.east_west);
718 start_nor = MAX(min_nor, dem->min_north);
719 end_nor = MIN(max_nor, dem->max_north);
720 if ( tleft.utm_zone == dem->utm_zone && bleft.utm_zone == dem->utm_zone
721 && (tleft.utm_letter >= 'N') == (dem->utm_letter >= 'N')
722 && (bleft.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
723 start_eas = MAX(min_eas, dem->min_east);
725 start_eas = dem->min_east;
726 if ( tright.utm_zone == dem->utm_zone && bright.utm_zone == dem->utm_zone
727 && (tright.utm_letter >= 'N') == (dem->utm_letter >= 'N')
728 && (bright.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
729 end_eas = MIN(max_eas, dem->max_east);
731 end_eas = dem->max_east;
733 start_nor = floor(start_nor / dem->north_scale) * dem->north_scale;
734 end_nor = ceil (end_nor / dem->north_scale) * dem->north_scale;
735 start_eas = floor(start_eas / dem->east_scale) * dem->east_scale;
736 end_eas = ceil (end_eas / dem->east_scale) * dem->east_scale;
738 vik_dem_east_north_to_xy ( dem, start_eas, start_nor, &start_x, &start_y );
740 /* TODO: why start_x and start_y are -1 -- rounding error from above? */
742 counter.zone = dem->utm_zone;
743 counter.letter = dem->utm_letter;
745 for ( x=start_x, counter.easting = start_eas; counter.easting <= end_eas; counter.easting += dem->east_scale * skip_factor, x += skip_factor ) {
746 if ( x > 0 && x < dem->n_columns ) {
747 column = g_ptr_array_index ( dem->columns, x );
748 for ( y=start_y, counter.northing = start_nor; counter.northing <= end_nor; counter.northing += dem->north_scale * skip_factor, y += skip_factor ) {
749 if ( y > column->n_points )
751 elev = column->points[y];
752 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev )
754 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
759 vik_coord_load_from_utm(&tmp, vik_viewport_get_coord_mode(vp), &counter);
760 vik_viewport_coord_to_screen(vp, &tmp, &a, &b);
761 if ( elev == VIK_DEM_INVALID_ELEVATION )
762 ; /* don't draw it */
763 else if ( elev <= 0 )
764 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, a-1, b-1, 2, 2 );
766 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 );
774 /* return the continent for the specified lat, lon */
776 static const gchar *srtm_continent_dir ( gint lat, gint lon )
778 extern const char *_srtm_continent_data[];
779 static GHashTable *srtm_continent = NULL;
780 const gchar *continent;
783 if (!srtm_continent) {
786 srtm_continent = g_hash_table_new(g_str_hash, g_str_equal);
787 s = _srtm_continent_data;
788 while (*s != (gchar *)-1) {
791 g_hash_table_insert(srtm_continent, (gpointer) *s, (gpointer) continent);
797 g_snprintf(name, sizeof(name), "%c%02d%c%03d",
798 (lat >= 0) ? 'N' : 'S', ABS(lat),
799 (lon >= 0) ? 'E' : 'W', ABS(lon));
801 return(g_hash_table_lookup(srtm_continent, name));
804 void vik_dem_layer_draw ( VikDEMLayer *vdl, gpointer data )
806 VikViewport *vp = (VikViewport *) data;
807 GList *dems_iter = vdl->files;
811 /* search for SRTM3 90m */
813 if ( vdl->source == DEM_SOURCE_SRTM )
814 srtm_draw_existence ( vp );
815 #ifdef VIK_CONFIG_DEM24K
816 else if ( vdl->source == DEM_SOURCE_DEM24K )
817 dem24k_draw_existence ( vp );
820 while ( dems_iter ) {
821 dem = a_dems_get ( (const char *) (dems_iter->data) );
823 vik_dem_layer_draw_dem ( vdl, vp, dem );
824 dems_iter = dems_iter->next;
828 void vik_dem_layer_free ( VikDEMLayer *vdl )
831 if ( vdl->color != NULL )
832 g_object_unref ( vdl->color );
835 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
836 g_object_unref ( vdl->gcs[i] );
839 if ( vdl->gcsgradient )
840 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
841 g_object_unref ( vdl->gcsgradient[i] );
842 g_free ( vdl->gcsgradient );
844 a_dems_list_free ( vdl->files );
847 VikDEMLayer *vik_dem_layer_create ( VikViewport *vp )
849 VikDEMLayer *vdl = vik_dem_layer_new ();
852 /* TODO: share GCS between layers */
853 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
854 vdl->gcs[i] = vik_viewport_new_gc ( vp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
856 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
857 vdl->gcsgradient[i] = vik_viewport_new_gc ( vp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
861 /**************************************************************
862 **** SOURCES & DOWNLOADING
863 **************************************************************/
869 VikDEMLayer *vdl; /* NULL if not alive */
875 /**************************************************
877 **************************************************/
879 static void srtm_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
882 const gchar *continent_dir;
884 intlat = (int)floor(p->lat);
885 intlon = (int)floor(p->lon);
886 continent_dir = srtm_continent_dir(intlat, intlon);
888 if (!continent_dir) {
889 g_warning(N_("No SRTM data available for %f, %f"), p->lat, p->lon);
893 gchar *src_fn = g_strdup_printf("%s%s/%c%02d%c%03d.hgt.zip",
896 (intlat >= 0) ? 'N' : 'S',
898 (intlon >= 0) ? 'E' : 'W',
901 static DownloadMapOptions options = { FALSE, FALSE, NULL, 0, a_check_map_file };
902 a_http_download_get_url ( SRTM_HTTP_SITE, src_fn, p->dest, &options, NULL );
906 static gchar *srtm_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
909 const gchar *continent_dir;
911 intlat = (int)floor(lat);
912 intlon = (int)floor(lon);
913 continent_dir = srtm_continent_dir(intlat, intlon);
916 continent_dir = "nowhere";
918 return g_strdup_printf("srtm3-%s%s%c%02d%c%03d.hgt.zip",
921 (intlat >= 0) ? 'N' : 'S',
923 (intlon >= 0) ? 'E' : 'W',
928 /* TODO: generalize */
929 static void srtm_draw_existence ( VikViewport *vp )
931 gdouble max_lat, max_lon, min_lat, min_lon;
932 gchar buf[strlen(MAPS_CACHE_DIR)+strlen(SRTM_CACHE_TEMPLATE)+30];
935 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
937 for (i = floor(min_lat); i <= floor(max_lat); i++) {
938 for (j = floor(min_lon); j <= floor(max_lon); j++) {
939 const gchar *continent_dir;
940 if ((continent_dir = srtm_continent_dir(i, j)) == NULL)
942 g_snprintf(buf, sizeof(buf), SRTM_CACHE_TEMPLATE,
946 (i >= 0) ? 'N' : 'S',
948 (j >= 0) ? 'E' : 'W',
950 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
955 sw.mode = VIK_COORD_LATLON;
956 ne.north_south = i+1;
958 ne.mode = VIK_COORD_LATLON;
959 vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
960 vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
961 if ( x1 < 0 ) x1 = 0;
962 if ( y2 < 0 ) y2 = 0;
963 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
964 FALSE, x1, y2, x2-x1, y1-y2 );
971 /**************************************************
973 **************************************************/
975 #ifdef VIK_CONFIG_DEM24K
977 static void dem24k_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
980 gchar *cmdline = g_strdup_printf("%s %.03f %.03f",
981 DEM24K_DOWNLOAD_SCRIPT,
984 /* FIX: don't use system, use execv or something. check for existence */
989 static gchar *dem24k_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
991 return g_strdup_printf("dem24k/%d/%d/%.03f,%.03f.dem",
998 /* TODO: generalize */
999 static void dem24k_draw_existence ( VikViewport *vp )
1001 gdouble max_lat, max_lon, min_lat, min_lon;
1002 gchar buf[strlen(MAPS_CACHE_DIR)+40];
1005 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
1007 for (i = floor(min_lat*8)/8; i <= floor(max_lat*8)/8; i+=0.125) {
1008 /* check lat dir first -- faster */
1009 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/",
1012 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1014 for (j = floor(min_lon*8)/8; j <= floor(max_lon*8)/8; j+=0.125) {
1015 /* check lon dir first -- faster */
1016 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/",
1020 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1022 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/%.03f,%.03f.dem",
1028 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1030 gint x1, y1, x2, y2;
1032 sw.east_west = j-0.125;
1033 sw.mode = VIK_COORD_LATLON;
1034 ne.north_south = i+0.125;
1036 ne.mode = VIK_COORD_LATLON;
1037 vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
1038 vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
1039 if ( x1 < 0 ) x1 = 0;
1040 if ( y2 < 0 ) y2 = 0;
1041 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
1042 FALSE, x1, y2, x2-x1, y1-y2 );
1049 /**************************************************
1050 * SOURCES -- DOWNLOADING & IMPORTING TOOL *
1051 **************************************************
1054 static void weak_ref_cb ( gpointer ptr, GObject * dead_vdl )
1056 DEMDownloadParams *p = ptr;
1057 g_mutex_lock ( p->mutex );
1059 g_mutex_unlock ( p->mutex );
1062 /* Try to add file full_path.
1063 * full_path will be copied.
1064 * returns FALSE if file does not exists, TRUE otherwise.
1066 static gboolean dem_layer_add_file ( VikDEMLayer *vdl, const gchar *full_path )
1068 if ( g_file_test(full_path, G_FILE_TEST_EXISTS ) == TRUE ) {
1069 /* only load if file size is not 0 (not in progress) */
1071 stat (full_path, &sb);
1073 gchar *duped_path = g_strdup(full_path);
1074 vdl->files = g_list_prepend ( vdl->files, duped_path );
1075 a_dems_load ( duped_path );
1076 g_debug("%s: %s", __FUNCTION__, duped_path);
1083 static void dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
1085 if ( p->source == DEM_SOURCE_SRTM )
1086 srtm_dem_download_thread ( p, threaddata );
1087 #ifdef VIK_CONFIG_DEM24K
1088 else if ( p->source == DEM_SOURCE_DEM24K )
1089 dem24k_dem_download_thread ( p, threaddata );
1094 //gdk_threads_enter();
1095 g_mutex_lock ( p->mutex );
1097 g_object_weak_unref ( G_OBJECT(p->vdl), weak_ref_cb, p );
1099 if ( dem_layer_add_file ( p->vdl, p->dest ) )
1100 vik_layer_emit_update ( VIK_LAYER(p->vdl), TRUE ); // Yes update from background thread
1102 g_mutex_unlock ( p->mutex );
1103 //gdk_threads_leave();
1107 static void free_dem_download_params ( DEMDownloadParams *p )
1109 g_mutex_free ( p->mutex );
1114 static gpointer dem_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1120 static gboolean dem_layer_download_release ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1126 gchar *dem_file = NULL;
1128 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1129 vik_coord_to_latlon ( &coord, &ll );
1132 if ( vdl->source == DEM_SOURCE_SRTM )
1133 dem_file = srtm_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1134 #ifdef VIK_CONFIG_DEM24K
1135 else if ( vdl->source == DEM_SOURCE_DEM24K )
1136 dem_file = dem24k_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1142 full_path = g_strdup_printf("%s%s", MAPS_CACHE_DIR, dem_file );
1144 g_debug("%s: %s", __FUNCTION__, full_path);
1146 // TODO: check if already in filelist
1148 if ( ! dem_layer_add_file(vdl, full_path) ) {
1149 gchar *tmp = g_strdup_printf ( _("Downloading DEM %s"), dem_file );
1150 DEMDownloadParams *p = g_malloc(sizeof(DEMDownloadParams));
1151 p->dest = g_strdup(full_path);
1155 p->mutex = g_mutex_new();
1156 p->source = vdl->source;
1157 g_object_weak_ref(G_OBJECT(p->vdl), weak_ref_cb, p );
1159 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vdl), tmp,
1160 (vik_thr_func) dem_download_thread, p,
1161 (vik_thr_free_func) free_dem_download_params, NULL, 1 );
1166 vik_layer_emit_update ( VIK_LAYER(vdl), FALSE );
1168 g_free ( dem_file );
1169 g_free ( full_path );
1174 static gboolean dem_layer_download_click ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1176 /* choose & keep track of cache dir
1177 * download in background thread
1178 * download over area */