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, GDK_CURSOR_IS_PIXMAP, &cursor_demdl_pixbuf },
142 The first entry is blue for a default 'sea' colour,
143 however the value used by the corresponding gc can be configured as part of the DEM layer properties.
144 The other colours, shaded from brown to white are used to give an indication of height.
146 static gchar *dem_height_colors[] = {
148 "#9b793c", "#9c7d40", "#9d8144", "#9e8549", "#9f894d", "#a08d51", "#a29156", "#a3955a", "#a4995e", "#a69d63",
149 "#a89f65", "#aaa267", "#ada569", "#afa76b", "#b1aa6d", "#b4ad6f", "#b6b071", "#b9b373", "#bcb676", "#beb978",
150 "#c0bc7a", "#c2c07d", "#c4c37f", "#c6c681", "#c8ca84", "#cacd86", "#ccd188", "#cfd58b", "#c2ce84", "#b5c87e",
151 "#a9c278", "#9cbb71", "#8fb56b", "#83af65", "#76a95e", "#6aa358", "#5e9d52", "#63a055", "#69a458", "#6fa85c",
152 "#74ac5f", "#7ab063", "#80b467", "#86b86a", "#8cbc6e", "#92c072", "#94c175", "#97c278", "#9ac47c", "#9cc57f",
153 "#9fc682", "#a2c886", "#a4c989", "#a7cb8d", "#aacd91", "#afce99", "#b5d0a1", "#bbd2aa", "#c0d3b2", "#c6d5ba",
154 "#ccd7c3", "#d1d9cb", "#d7dbd4", "#DDDDDD", "#e0e0e0", "#e4e4e4", "#e8e8e8", "#ebebeb", "#efefef", "#f3f3f3",
155 "#f7f7f7", "#fbfbfb", "#ffffff"
158 static const guint DEM_N_HEIGHT_COLORS = sizeof(dem_height_colors)/sizeof(dem_height_colors[0]);
161 "#9b793c", "#9e8549", "#a29156", "#a69d63", "#ada569", "#b4ad6f", "#bcb676", "#c2c07d", "#c8ca84", "#cfd58b",
162 "#a9c278", "#83af65", "#5e9d52", "#6fa85c", "#80b467", "#92c072", "#9ac47c", "#a2c886", "#aacd91", "#bbd2aa",
163 "#ccd7c3", "#DDDDDD", "#e8e8e8", "#f3f3f3", "#FFFFFF"
167 static gchar *dem_gradient_colors[] = {
169 "#000000", "#000011", "#000022", "#000033", "#000044", "#00004c", "#000055", "#00005d", "#000066", "#00006e",
170 "#000077", "#00007f", "#000088", "#000090", "#000099", "#0000a1", "#0000aa", "#0000b2", "#0000bb", "#0000c3",
171 "#0000cc", "#0000d4", "#0000dd", "#0000e5", "#0000ee", "#0000f6", "#0000ff", "#0008f7", "#0011ee", "#0019e6",
172 "#0022dd", "#002ad5", "#0033cc", "#003bc4", "#0044bb", "#004cb3", "#0055aa", "#005da2", "#006699", "#006e91",
173 "#007788", "#007f80", "#008877", "#00906f", "#009966", "#00a15e", "#00aa55", "#00b24d", "#00bb44", "#00c33c",
174 "#00cc33", "#00d42b", "#00dd22", "#00e51a", "#00ee11", "#00f609", "#00ff00", "#08f700", "#11ee00", "#19e600",
175 "#22dd00", "#2ad500", "#33cc00", "#3bc400", "#44bb00", "#4cb300", "#55aa00", "#5da200", "#669900", "#6e9100",
176 "#778800", "#7f8000", "#887700", "#906f00", "#996600", "#a15e00", "#aa5500", "#b24d00", "#bb4400", "#c33c00",
177 "#cc3300", "#d42b00", "#dd2200", "#e51a00", "#ee1100", "#f60900", "#ff0000",
181 static const guint DEM_N_GRADIENT_COLORS = sizeof(dem_gradient_colors)/sizeof(dem_gradient_colors[0]);
184 VikLayerInterface vik_dem_layer_interface = {
189 sizeof(dem_tools) / sizeof(dem_tools[0]),
198 (VikLayerFuncCreate) vik_dem_layer_create,
199 (VikLayerFuncRealize) NULL,
200 (VikLayerFuncPostRead) dem_layer_post_read,
201 (VikLayerFuncFree) vik_dem_layer_free,
203 (VikLayerFuncProperties) NULL,
204 (VikLayerFuncDraw) vik_dem_layer_draw,
205 (VikLayerFuncChangeCoordMode) NULL,
207 (VikLayerFuncSetMenuItemsSelection) NULL,
208 (VikLayerFuncGetMenuItemsSelection) NULL,
210 (VikLayerFuncAddMenuItems) NULL,
211 (VikLayerFuncSublayerAddMenuItems) NULL,
213 (VikLayerFuncSublayerRenameRequest) NULL,
214 (VikLayerFuncSublayerToggleVisible) NULL,
215 (VikLayerFuncSublayerTooltip) NULL,
216 (VikLayerFuncLayerTooltip) dem_layer_tooltip,
217 (VikLayerFuncLayerSelected) NULL,
219 (VikLayerFuncMarshall) dem_layer_marshall,
220 (VikLayerFuncUnmarshall) dem_layer_unmarshall,
222 (VikLayerFuncSetParam) dem_layer_set_param,
223 (VikLayerFuncGetParam) dem_layer_get_param,
225 (VikLayerFuncReadFileData) NULL,
226 (VikLayerFuncWriteFileData) NULL,
228 (VikLayerFuncDeleteItem) NULL,
229 (VikLayerFuncCutItem) NULL,
230 (VikLayerFuncCopyItem) NULL,
231 (VikLayerFuncPasteItem) NULL,
232 (VikLayerFuncFreeCopiedItem) NULL,
233 (VikLayerFuncDragDropRequest) NULL,
235 (VikLayerFuncSelectClick) NULL,
236 (VikLayerFuncSelectMove) NULL,
237 (VikLayerFuncSelectRelease) NULL,
238 (VikLayerFuncSelectedViewportMenu) NULL,
241 struct _VikDEMLayer {
253 GType vik_dem_layer_get_type ()
255 static GType vdl_type = 0;
259 static const GTypeInfo vdl_info =
261 sizeof (VikDEMLayerClass),
262 NULL, /* base_init */
263 NULL, /* base_finalize */
264 NULL, /* class init */
265 NULL, /* class_finalize */
266 NULL, /* class_data */
267 sizeof (VikDEMLayer),
269 NULL /* instance init */
271 vdl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikDEMLayer", &vdl_info, 0 );
277 static const gchar* dem_layer_tooltip( VikDEMLayer *vdl )
279 static gchar tmp_buf[100];
280 g_snprintf (tmp_buf, sizeof(tmp_buf), _("Number of files: %d"), g_list_length (vdl->files));
284 static void dem_layer_marshall( VikDEMLayer *vdl, guint8 **data, gint *len )
286 vik_layer_marshall_params ( VIK_LAYER(vdl), data, len );
289 static VikDEMLayer *dem_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
291 VikDEMLayer *rv = vik_dem_layer_new ();
294 /* TODO: share GCS between layers */
295 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
296 rv->gcs[i] = vik_viewport_new_gc ( vvp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
298 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
299 rv->gcsgradient[i] = vik_viewport_new_gc ( vvp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
301 vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
305 /* Structure for DEM data used in background thread */
308 } dem_load_thread_data;
311 * Function for starting the DEM file loading as a background thread
313 static int dem_layer_load_list_thread ( dem_load_thread_data *dltd, gpointer threaddata )
315 int result = 0; // Default to good
317 if ( a_dems_load_list ( &(dltd->vdl->files), threaddata ) ) {
322 // ATM as each file is processed the screen is not updated (no mechanism exposed to a_dems_load_list)
323 // Thus force draw only at the end, as loading is complete/aborted
324 //gdk_threads_enter();
325 // Test is helpful to prevent Gtk-CRITICAL warnings if the program is exitted whilst loading
326 if ( IS_VIK_LAYER(dltd->vdl) )
327 vik_layer_emit_update ( VIK_LAYER(dltd->vdl), TRUE ); // Yes update from background thread
328 //gdk_threads_leave();
333 static void dem_layer_thread_data_free ( dem_load_thread_data *data )
339 static void dem_layer_thread_cancel ( dem_load_thread_data *data )
342 // Instead of freeing the list, leave it as partially processed
343 // Thus we can see/use what was done
346 gboolean dem_layer_set_param ( VikDEMLayer *vdl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
350 case PARAM_COLOR: gdk_gc_set_rgb_fg_color ( vdl->gcs[0], &(data.c) ); break;
351 case PARAM_SOURCE: vdl->source = data.u; break;
352 case PARAM_TYPE: vdl->type = data.u; break;
354 /* Convert to store internally
355 NB file operation always in internal units (metres) */
356 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
357 vdl->min_elev = VIK_FEET_TO_METERS(data.d);
359 vdl->min_elev = data.d;
362 /* Convert to store internally
363 NB file operation always in internal units (metres) */
364 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
365 vdl->max_elev = VIK_FEET_TO_METERS(data.d);
367 vdl->max_elev = data.d;
371 // Clear out old settings - if any commonalities with new settings they will have to be read again
372 a_dems_list_free ( vdl->files );
373 // Set file list so any other intermediate screen drawing updates will show currently loaded DEMs by the working thread
374 vdl->files = data.sl;
376 dem_load_thread_data *dltd = g_malloc ( sizeof(dem_load_thread_data) );
378 dltd->vdl->files = data.sl;
380 a_background_thread ( VIK_GTK_WINDOW_FROM_WIDGET(vp),
382 (vik_thr_func) dem_layer_load_list_thread,
384 (vik_thr_free_func) dem_layer_thread_data_free,
385 (vik_thr_free_func) dem_layer_thread_cancel,
386 g_list_length ( data.sl ) ); // Number of DEM files
393 static VikLayerParamData dem_layer_get_param ( VikDEMLayer *vdl, guint16 id, gboolean is_file_operation )
395 VikLayerParamData rv;
398 case PARAM_FILES: rv.sl = vdl->files; break;
399 case PARAM_SOURCE: rv.u = vdl->source; break;
400 case PARAM_TYPE: rv.u = vdl->type; break;
401 case PARAM_COLOR: vik_gc_get_fg_color ( vdl->gcs[0], &(rv.c) ); break;
403 /* Convert for display in desired units
404 NB file operation always in internal units (metres) */
405 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
406 rv.d = VIK_METERS_TO_FEET(vdl->min_elev);
408 rv.d = vdl->min_elev;
411 /* Convert for display in desired units
412 NB file operation always in internal units (metres) */
413 if (!is_file_operation && a_vik_get_units_height () == VIK_UNITS_HEIGHT_FEET )
414 rv.d = VIK_METERS_TO_FEET(vdl->max_elev);
416 rv.d = vdl->max_elev;
422 static void dem_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file )
424 /* nothing ATM, but keep in case it's needed the future */
427 VikDEMLayer *vik_dem_layer_new ( )
429 VikDEMLayer *vdl = VIK_DEM_LAYER ( g_object_new ( VIK_DEM_LAYER_TYPE, NULL ) );
431 vik_layer_init ( VIK_LAYER(vdl), VIK_LAYER_DEM );
435 vdl->gcs = g_malloc(sizeof(GdkGC *)*DEM_N_HEIGHT_COLORS);
436 vdl->gcsgradient = g_malloc(sizeof(GdkGC *)*DEM_N_GRADIENT_COLORS);
437 /* make new gcs only if we need it (copy layer -> use old) */
440 vdl->max_elev = 1000.0;
441 vdl->source = DEM_SOURCE_SRTM;
442 vdl->type = DEM_TYPE_HEIGHT;
447 static inline guint16 get_height_difference(gint16 elev, gint16 new_elev)
449 if(new_elev == VIK_DEM_INVALID_ELEVATION)
452 return abs(new_elev - elev);
456 static void vik_dem_layer_draw_dem ( VikDEMLayer *vdl, VikViewport *vp, VikDEM *dem )
458 VikDEMColumn *column, *prevcolumn, *nextcolumn;
460 struct LatLon dem_northeast, dem_southwest;
461 gdouble max_lat, max_lon, min_lat, min_lon;
463 /**** Check if viewport and DEM data overlap ****/
465 /* get min, max lat/lon of viewport */
466 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
468 /* get min, max lat/lon of DEM data */
469 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
470 dem_northeast.lat = dem->max_north / 3600.0;
471 dem_northeast.lon = dem->max_east / 3600.0;
472 dem_southwest.lat = dem->min_north / 3600.0;
473 dem_southwest.lon = dem->min_east / 3600.0;
474 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
475 struct UTM dem_northeast_utm, dem_southwest_utm;
476 dem_northeast_utm.northing = dem->max_north;
477 dem_northeast_utm.easting = dem->max_east;
478 dem_southwest_utm.northing = dem->min_north;
479 dem_southwest_utm.easting = dem->min_east;
480 dem_northeast_utm.zone = dem_southwest_utm.zone = dem->utm_zone;
481 dem_northeast_utm.letter = dem_southwest_utm.letter = dem->utm_letter;
483 a_coords_utm_to_latlon(&dem_northeast_utm, &dem_northeast);
484 a_coords_utm_to_latlon(&dem_southwest_utm, &dem_southwest);
487 if ( (max_lat > dem_northeast.lat && min_lat > dem_northeast.lat) ||
488 (max_lat < dem_southwest.lat && min_lat < dem_southwest.lat) )
490 else if ( (max_lon > dem_northeast.lon && min_lon > dem_northeast.lon) ||
491 (max_lon < dem_southwest.lon && min_lon < dem_southwest.lon) )
493 /* else they overlap */
495 /**** End Overlap Check ****/
496 /* boxes to show where we have DEM instead of actually drawing the DEM.
497 * useful if we want to see what areas we have coverage for (if we want
498 * to get elevation data for a track) but don't want to cover the map.
502 /* draw a box if a DEM is loaded. in future I'd like to add an option for this
503 * this is useful if we want to see what areas we have dem for but don't want to
504 * cover the map (or maybe we just need translucent DEM?) */
506 VikCoord demne, demsw;
508 vik_coord_load_from_latlon(&demne, vik_viewport_get_coord_mode(vp), &dem_northeast);
509 vik_coord_load_from_latlon(&demsw, vik_viewport_get_coord_mode(vp), &dem_southwest);
511 vik_viewport_coord_to_screen ( vp, &demne, &x1, &y1 );
512 vik_viewport_coord_to_screen ( vp, &demsw, &x2, &y2 );
514 if ( x1 > vik_viewport_get_width(vp) ) x1=vik_viewport_get_width(vp);
515 if ( y2 > vik_viewport_get_height(vp) ) y2=vik_viewport_get_height(vp);
516 if ( x2 < 0 ) x2 = 0;
517 if ( y1 < 0 ) y1 = 0;
518 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
519 FALSE, x2, y1, x1-x2, y2-y1 );
524 if ( dem->horiz_units == VIK_DEM_HORIZ_LL_ARCSECONDS ) {
525 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
527 gdouble max_lat_as, max_lon_as, min_lat_as, min_lon_as;
528 gdouble start_lat_as, end_lat_as, start_lon_as, end_lon_as;
530 gdouble start_lat, end_lat, start_lon, end_lon;
532 struct LatLon counter;
534 guint x, y, start_x, start_y;
538 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 80 ); /* todo: smarter calculation. */
540 gdouble nscale_deg = dem->north_scale / ((gdouble) 3600);
541 gdouble escale_deg = dem->east_scale / ((gdouble) 3600);
543 max_lat_as = max_lat * 3600;
544 min_lat_as = min_lat * 3600;
545 max_lon_as = max_lon * 3600;
546 min_lon_as = min_lon * 3600;
548 start_lat_as = MAX(min_lat_as, dem->min_north);
549 end_lat_as = MIN(max_lat_as, dem->max_north);
550 start_lon_as = MAX(min_lon_as, dem->min_east);
551 end_lon_as = MIN(max_lon_as, dem->max_east);
553 start_lat = floor(start_lat_as / dem->north_scale) * nscale_deg;
554 end_lat = ceil (end_lat_as / dem->north_scale) * nscale_deg;
555 start_lon = floor(start_lon_as / dem->east_scale) * escale_deg;
556 end_lon = ceil (end_lon_as / dem->east_scale) * escale_deg;
558 vik_dem_east_north_to_xy ( dem, start_lon_as, start_lat_as, &start_x, &start_y );
559 guint gradient_skip_factor = 1;
560 if(vdl->type == DEM_TYPE_GRADIENT)
561 gradient_skip_factor = skip_factor;
563 /* verify sane elev interval */
564 if ( vdl->max_elev <= vdl->min_elev )
565 vdl->max_elev = vdl->min_elev + 1;
567 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 ) {
568 // NOTE: ( counter.lon <= end_lon + ESCALE_DEG*SKIP_FACTOR ) is neccessary so in high zoom modes,
569 // the leftmost column does also get drawn, if the center point is out of viewport.
570 if ( x >= 0 && x < dem->n_columns ) {
571 column = g_ptr_array_index ( dem->columns, x );
572 // get previous and next column. catch out-of-bound.
574 new_x -= gradient_skip_factor;
576 prevcolumn = g_ptr_array_index ( dem->columns, x+1);
578 prevcolumn = g_ptr_array_index ( dem->columns, new_x);
580 new_x += gradient_skip_factor;
581 if(new_x >= dem->n_columns)
582 nextcolumn = g_ptr_array_index ( dem->columns, x-1);
584 nextcolumn = g_ptr_array_index ( dem->columns, new_x);
586 for ( y=start_y, counter.lat = start_lat; counter.lat <= end_lat; counter.lat += nscale_deg * skip_factor, y += skip_factor ) {
587 if ( y > column->n_points )
590 elev = column->points[y];
592 // calculate bounding box for drawing
593 gint box_x, box_y, box_width, box_height;
596 box_c.lat += (nscale_deg * skip_factor)/2;
597 box_c.lon -= (escale_deg * skip_factor)/2;
598 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
599 vik_viewport_coord_to_screen(vp, &tmp, &box_x, &box_y);
600 // catch box at borders
605 box_c.lat -= nscale_deg * skip_factor;
606 box_c.lon += escale_deg * skip_factor;
607 vik_coord_load_from_latlon(&tmp, vik_viewport_get_coord_mode(vp), &box_c);
608 vik_viewport_coord_to_screen(vp, &tmp, &box_width, &box_height);
611 // catch box at borders
612 if(box_width < 0 || box_height < 0)
613 continue; // skip this. this is out of our viewport anyway. FIXME: why?
615 gboolean below_minimum = FALSE;
616 if(vdl->type == DEM_TYPE_HEIGHT) {
617 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev ) {
618 // Prevent 'elev - vdl->min_elev' from being negative so can safely use as array index
619 elev = ceil ( vdl->min_elev );
620 below_minimum = TRUE;
622 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
623 elev = vdl->max_elev;
627 if(box_width < 0 || box_height < 0) // FIXME: why does this happen?
630 if(vdl->type == DEM_TYPE_GRADIENT) {
631 if( elev == VIK_DEM_INVALID_ELEVATION ) {
634 // calculate and sum gradient in all directions
638 // calculate gradient from height points all around the current one
639 new_y = y - gradient_skip_factor;
642 change += get_height_difference(elev, prevcolumn->points[new_y]);
643 change += get_height_difference(elev, column->points[new_y]);
644 change += get_height_difference(elev, nextcolumn->points[new_y]);
646 change += get_height_difference(elev, prevcolumn->points[y]);
647 change += get_height_difference(elev, nextcolumn->points[y]);
649 new_y = y + gradient_skip_factor;
650 if(new_y >= column->n_points)
652 change += get_height_difference(elev, prevcolumn->points[new_y]);
653 change += get_height_difference(elev, column->points[new_y]);
654 change += get_height_difference(elev, nextcolumn->points[new_y]);
656 change = change / ((skip_factor > 1) ? log(skip_factor) : 0.55); // FIXME: better calc.
658 if(change < vdl->min_elev)
659 // Prevent 'change - vdl->min_elev' from being negative so can safely use as array index
660 change = ceil ( vdl->min_elev );
662 if(change > vdl->max_elev)
663 change = vdl->max_elev;
665 // void vik_viewport_draw_rectangle ( VikViewport *vvp, GdkGC *gc, gboolean filled, gint x1, gint y1, gint x2, gint y2 );
666 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);
669 if(vdl->type == DEM_TYPE_HEIGHT) {
670 if ( elev == VIK_DEM_INVALID_ELEVATION )
671 ; /* don't draw it */
672 else if ( elev <= 0 || below_minimum )
673 /* If 'sea' colour or below the defined mininum draw in the configurable colour */
674 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, box_x, box_y, box_width, box_height);
676 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);
683 } else if ( dem->horiz_units == VIK_DEM_HORIZ_UTM_METERS ) {
684 gdouble max_nor, max_eas, min_nor, min_eas;
685 gdouble start_nor, start_eas, end_nor, end_eas;
689 guint x, y, start_x, start_y;
691 VikCoord tmp; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
694 guint skip_factor = ceil ( vik_viewport_get_xmpp(vp) / 10 ); /* todo: smarter calculation. */
696 VikCoord tleft, tright, bleft, bright;
698 vik_viewport_screen_to_coord ( vp, 0, 0, &tleft );
699 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), 0, &tright );
700 vik_viewport_screen_to_coord ( vp, 0, vik_viewport_get_height(vp), &bleft );
701 vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), vik_viewport_get_height(vp), &bright );
704 vik_coord_convert(&tleft, VIK_COORD_UTM);
705 vik_coord_convert(&tright, VIK_COORD_UTM);
706 vik_coord_convert(&bleft, VIK_COORD_UTM);
707 vik_coord_convert(&bright, VIK_COORD_UTM);
709 max_nor = MAX(tleft.north_south, tright.north_south);
710 min_nor = MIN(bleft.north_south, bright.north_south);
711 max_eas = MAX(bright.east_west, tright.east_west);
712 min_eas = MIN(bleft.east_west, tleft.east_west);
714 start_nor = MAX(min_nor, dem->min_north);
715 end_nor = MIN(max_nor, dem->max_north);
716 if ( tleft.utm_zone == dem->utm_zone && bleft.utm_zone == dem->utm_zone
717 && (tleft.utm_letter >= 'N') == (dem->utm_letter >= 'N')
718 && (bleft.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
719 start_eas = MAX(min_eas, dem->min_east);
721 start_eas = dem->min_east;
722 if ( tright.utm_zone == dem->utm_zone && bright.utm_zone == dem->utm_zone
723 && (tright.utm_letter >= 'N') == (dem->utm_letter >= 'N')
724 && (bright.utm_letter >= 'N') == (dem->utm_letter >= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
725 end_eas = MIN(max_eas, dem->max_east);
727 end_eas = dem->max_east;
729 start_nor = floor(start_nor / dem->north_scale) * dem->north_scale;
730 end_nor = ceil (end_nor / dem->north_scale) * dem->north_scale;
731 start_eas = floor(start_eas / dem->east_scale) * dem->east_scale;
732 end_eas = ceil (end_eas / dem->east_scale) * dem->east_scale;
734 vik_dem_east_north_to_xy ( dem, start_eas, start_nor, &start_x, &start_y );
736 /* TODO: why start_x and start_y are -1 -- rounding error from above? */
738 counter.zone = dem->utm_zone;
739 counter.letter = dem->utm_letter;
741 for ( x=start_x, counter.easting = start_eas; counter.easting <= end_eas; counter.easting += dem->east_scale * skip_factor, x += skip_factor ) {
742 if ( x > 0 && x < dem->n_columns ) {
743 column = g_ptr_array_index ( dem->columns, x );
744 for ( y=start_y, counter.northing = start_nor; counter.northing <= end_nor; counter.northing += dem->north_scale * skip_factor, y += skip_factor ) {
745 if ( y > column->n_points )
747 elev = column->points[y];
748 if ( elev != VIK_DEM_INVALID_ELEVATION && elev < vdl->min_elev )
750 if ( elev != VIK_DEM_INVALID_ELEVATION && elev > vdl->max_elev )
755 vik_coord_load_from_utm(&tmp, vik_viewport_get_coord_mode(vp), &counter);
756 vik_viewport_coord_to_screen(vp, &tmp, &a, &b);
757 if ( elev == VIK_DEM_INVALID_ELEVATION )
758 ; /* don't draw it */
759 else if ( elev <= 0 )
760 vik_viewport_draw_rectangle(vp, vdl->gcs[0], TRUE, a-1, b-1, 2, 2 );
762 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 );
770 /* return the continent for the specified lat, lon */
772 static const gchar *srtm_continent_dir ( gint lat, gint lon )
774 extern const char *_srtm_continent_data[];
775 static GHashTable *srtm_continent = NULL;
776 const gchar *continent;
779 if (!srtm_continent) {
782 srtm_continent = g_hash_table_new(g_str_hash, g_str_equal);
783 s = _srtm_continent_data;
784 while (*s != (gchar *)-1) {
787 g_hash_table_insert(srtm_continent, (gpointer) *s, (gpointer) continent);
793 g_snprintf(name, sizeof(name), "%c%02d%c%03d",
794 (lat >= 0) ? 'N' : 'S', ABS(lat),
795 (lon >= 0) ? 'E' : 'W', ABS(lon));
797 return(g_hash_table_lookup(srtm_continent, name));
800 void vik_dem_layer_draw ( VikDEMLayer *vdl, gpointer data )
802 VikViewport *vp = (VikViewport *) data;
803 GList *dems_iter = vdl->files;
807 /* search for SRTM3 90m */
809 if ( vdl->source == DEM_SOURCE_SRTM )
810 srtm_draw_existence ( vp );
811 #ifdef VIK_CONFIG_DEM24K
812 else if ( vdl->source == DEM_SOURCE_DEM24K )
813 dem24k_draw_existence ( vp );
816 while ( dems_iter ) {
817 dem = a_dems_get ( (const char *) (dems_iter->data) );
819 vik_dem_layer_draw_dem ( vdl, vp, dem );
820 dems_iter = dems_iter->next;
824 void vik_dem_layer_free ( VikDEMLayer *vdl )
827 if ( vdl->color != NULL )
828 g_object_unref ( vdl->color );
831 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
832 g_object_unref ( vdl->gcs[i] );
835 if ( vdl->gcsgradient )
836 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
837 g_object_unref ( vdl->gcsgradient[i] );
838 g_free ( vdl->gcsgradient );
840 a_dems_list_free ( vdl->files );
843 VikDEMLayer *vik_dem_layer_create ( VikViewport *vp )
845 VikDEMLayer *vdl = vik_dem_layer_new ();
848 /* TODO: share GCS between layers */
849 for ( i = 0; i < DEM_N_HEIGHT_COLORS; i++ )
850 vdl->gcs[i] = vik_viewport_new_gc ( vp, dem_height_colors[i], UNUSED_LINE_THICKNESS );
852 for ( i = 0; i < DEM_N_GRADIENT_COLORS; i++ )
853 vdl->gcsgradient[i] = vik_viewport_new_gc ( vp, dem_gradient_colors[i], UNUSED_LINE_THICKNESS );
857 /**************************************************************
858 **** SOURCES & DOWNLOADING
859 **************************************************************/
865 VikDEMLayer *vdl; /* NULL if not alive */
871 /**************************************************
873 **************************************************/
875 static void srtm_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
878 const gchar *continent_dir;
880 intlat = (int)floor(p->lat);
881 intlon = (int)floor(p->lon);
882 continent_dir = srtm_continent_dir(intlat, intlon);
884 if (!continent_dir) {
885 g_warning(N_("No SRTM data available for %f, %f"), p->lat, p->lon);
889 gchar *src_fn = g_strdup_printf("%s%s/%c%02d%c%03d.hgt.zip",
892 (intlat >= 0) ? 'N' : 'S',
894 (intlon >= 0) ? 'E' : 'W',
897 static DownloadMapOptions options = { FALSE, FALSE, NULL, 0, a_check_map_file };
898 a_http_download_get_url ( SRTM_HTTP_SITE, src_fn, p->dest, &options, NULL );
902 static gchar *srtm_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
905 const gchar *continent_dir;
907 intlat = (int)floor(lat);
908 intlon = (int)floor(lon);
909 continent_dir = srtm_continent_dir(intlat, intlon);
912 continent_dir = "nowhere";
914 return g_strdup_printf("srtm3-%s%s%c%02d%c%03d.hgt.zip",
917 (intlat >= 0) ? 'N' : 'S',
919 (intlon >= 0) ? 'E' : 'W',
924 /* TODO: generalize */
925 static void srtm_draw_existence ( VikViewport *vp )
927 gdouble max_lat, max_lon, min_lat, min_lon;
928 gchar buf[strlen(MAPS_CACHE_DIR)+strlen(SRTM_CACHE_TEMPLATE)+30];
931 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
933 for (i = floor(min_lat); i <= floor(max_lat); i++) {
934 for (j = floor(min_lon); j <= floor(max_lon); j++) {
935 const gchar *continent_dir;
936 if ((continent_dir = srtm_continent_dir(i, j)) == NULL)
938 g_snprintf(buf, sizeof(buf), SRTM_CACHE_TEMPLATE,
942 (i >= 0) ? 'N' : 'S',
944 (j >= 0) ? 'E' : 'W',
946 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
951 sw.mode = VIK_COORD_LATLON;
952 ne.north_south = i+1;
954 ne.mode = VIK_COORD_LATLON;
955 vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
956 vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
957 if ( x1 < 0 ) x1 = 0;
958 if ( y2 < 0 ) y2 = 0;
959 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
960 FALSE, x1, y2, x2-x1, y1-y2 );
967 /**************************************************
969 **************************************************/
971 #ifdef VIK_CONFIG_DEM24K
973 static void dem24k_dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
976 gchar *cmdline = g_strdup_printf("%s %.03f %.03f",
977 DEM24K_DOWNLOAD_SCRIPT,
980 /* FIX: don't use system, use execv or something. check for existence */
985 static gchar *dem24k_lat_lon_to_dest_fn ( gdouble lat, gdouble lon )
987 return g_strdup_printf("dem24k/%d/%d/%.03f,%.03f.dem",
994 /* TODO: generalize */
995 static void dem24k_draw_existence ( VikViewport *vp )
997 gdouble max_lat, max_lon, min_lat, min_lon;
998 gchar buf[strlen(MAPS_CACHE_DIR)+40];
1001 vik_viewport_get_min_max_lat_lon ( vp, &min_lat, &max_lat, &min_lon, &max_lon );
1003 for (i = floor(min_lat*8)/8; i <= floor(max_lat*8)/8; i+=0.125) {
1004 /* check lat dir first -- faster */
1005 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/",
1008 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1010 for (j = floor(min_lon*8)/8; j <= floor(max_lon*8)/8; j+=0.125) {
1011 /* check lon dir first -- faster */
1012 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/",
1016 if ( g_file_test(buf, G_FILE_TEST_EXISTS) == FALSE )
1018 g_snprintf(buf, sizeof(buf), "%sdem24k/%d/%d/%.03f,%.03f.dem",
1024 if ( g_file_test(buf, G_FILE_TEST_EXISTS ) == TRUE ) {
1026 gint x1, y1, x2, y2;
1028 sw.east_west = j-0.125;
1029 sw.mode = VIK_COORD_LATLON;
1030 ne.north_south = i+0.125;
1032 ne.mode = VIK_COORD_LATLON;
1033 vik_viewport_coord_to_screen ( vp, &sw, &x1, &y1 );
1034 vik_viewport_coord_to_screen ( vp, &ne, &x2, &y2 );
1035 if ( x1 < 0 ) x1 = 0;
1036 if ( y2 < 0 ) y2 = 0;
1037 vik_viewport_draw_rectangle ( vp, GTK_WIDGET(vp)->style->black_gc,
1038 FALSE, x1, y2, x2-x1, y1-y2 );
1045 /**************************************************
1046 * SOURCES -- DOWNLOADING & IMPORTING TOOL *
1047 **************************************************
1050 static void weak_ref_cb ( gpointer ptr, GObject * dead_vdl )
1052 DEMDownloadParams *p = ptr;
1053 g_mutex_lock ( p->mutex );
1055 g_mutex_unlock ( p->mutex );
1058 /* Try to add file full_path.
1059 * full_path will be copied.
1060 * returns FALSE if file does not exists, TRUE otherwise.
1062 static gboolean dem_layer_add_file ( VikDEMLayer *vdl, const gchar *full_path )
1064 if ( g_file_test(full_path, G_FILE_TEST_EXISTS ) == TRUE ) {
1065 /* only load if file size is not 0 (not in progress) */
1067 stat (full_path, &sb);
1069 gchar *duped_path = g_strdup(full_path);
1070 vdl->files = g_list_prepend ( vdl->files, duped_path );
1071 a_dems_load ( duped_path );
1072 g_debug("%s: %s", __FUNCTION__, duped_path);
1079 static void dem_download_thread ( DEMDownloadParams *p, gpointer threaddata )
1081 if ( p->source == DEM_SOURCE_SRTM )
1082 srtm_dem_download_thread ( p, threaddata );
1083 #ifdef VIK_CONFIG_DEM24K
1084 else if ( p->source == DEM_SOURCE_DEM24K )
1085 dem24k_dem_download_thread ( p, threaddata );
1090 //gdk_threads_enter();
1091 g_mutex_lock ( p->mutex );
1093 g_object_weak_unref ( G_OBJECT(p->vdl), weak_ref_cb, p );
1095 if ( dem_layer_add_file ( p->vdl, p->dest ) )
1096 vik_layer_emit_update ( VIK_LAYER(p->vdl), TRUE ); // Yes update from background thread
1098 g_mutex_unlock ( p->mutex );
1099 //gdk_threads_leave();
1103 static void free_dem_download_params ( DEMDownloadParams *p )
1105 g_mutex_free ( p->mutex );
1110 static gpointer dem_layer_download_create ( VikWindow *vw, VikViewport *vvp)
1116 static gboolean dem_layer_download_release ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1122 gchar *dem_file = NULL;
1124 vik_viewport_screen_to_coord ( vvp, event->x, event->y, &coord );
1125 vik_coord_to_latlon ( &coord, &ll );
1128 if ( vdl->source == DEM_SOURCE_SRTM )
1129 dem_file = srtm_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1130 #ifdef VIK_CONFIG_DEM24K
1131 else if ( vdl->source == DEM_SOURCE_DEM24K )
1132 dem_file = dem24k_lat_lon_to_dest_fn ( ll.lat, ll.lon );
1138 full_path = g_strdup_printf("%s%s", MAPS_CACHE_DIR, dem_file );
1140 g_debug("%s: %s", __FUNCTION__, full_path);
1142 // TODO: check if already in filelist
1144 if ( ! dem_layer_add_file(vdl, full_path) ) {
1145 gchar *tmp = g_strdup_printf ( _("Downloading DEM %s"), dem_file );
1146 DEMDownloadParams *p = g_malloc(sizeof(DEMDownloadParams));
1147 p->dest = g_strdup(full_path);
1151 p->mutex = g_mutex_new();
1152 p->source = vdl->source;
1153 g_object_weak_ref(G_OBJECT(p->vdl), weak_ref_cb, p );
1155 a_background_thread ( VIK_GTK_WINDOW_FROM_LAYER(vdl), tmp,
1156 (vik_thr_func) dem_download_thread, p,
1157 (vik_thr_free_func) free_dem_download_params, NULL, 1 );
1162 vik_layer_emit_update ( VIK_LAYER(vdl), FALSE );
1164 g_free ( dem_file );
1165 g_free ( full_path );
1170 static gboolean dem_layer_download_click ( VikDEMLayer *vdl, GdkEventButton *event, VikViewport *vvp )
1172 /* choose & keep track of cache dir
1173 * download in background thread
1174 * download over area */