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
27 #include "vikwaypoint.h"
29 #include "vikviewport.h"
30 #include "viktreeview.h"
32 #include "vikaggregatelayer.h"
33 #include "viklayerspanel.h"
34 #include "vikdemlayer.h"
35 #include "vikdemlayer_pixmap.h"
40 static VikDEMLayer
*dem_layer_copy ( VikDEMLayer
*vdl
, gpointer vp
);
41 static void dem_layer_marshall( VikDEMLayer
*vdl
, guint8
**data
, gint
*len
);
42 static VikDEMLayer
*dem_layer_unmarshall( guint8
*data
, gint len
, VikViewport
*vvp
);
43 static gboolean
dem_layer_set_param ( VikDEMLayer
*vdl
, guint16 id
, VikLayerParamData data
, VikViewport
*vp
);
44 static VikLayerParamData
dem_layer_get_param ( VikDEMLayer
*vdl
, guint16 id
);
45 static void dem_layer_update_gc ( VikDEMLayer
*vdl
, VikViewport
*vp
, const gchar
*color
);
46 static void dem_layer_post_read ( VikDEMLayer
*vdl
, VikViewport
*vp
);
48 static VikLayerParamScale param_scales
[] = {
53 static VikLayerParam dem_layer_params
[] = {
54 { "files", VIK_LAYER_PARAM_STRING_LIST
, VIK_LAYER_GROUP_NONE
, "DEM Files:", VIK_LAYER_WIDGET_FILELIST
},
55 { "color", VIK_LAYER_PARAM_STRING
, VIK_LAYER_GROUP_NONE
, "Color:", VIK_LAYER_WIDGET_ENTRY
},
56 { "max_elev", VIK_LAYER_PARAM_DOUBLE
, VIK_LAYER_GROUP_NONE
, "Max Elev:", VIK_LAYER_WIDGET_SPINBUTTON
, param_scales
+ 0 },
57 { "line_thickness", VIK_LAYER_PARAM_UINT
, VIK_LAYER_GROUP_NONE
, "Line Thickness:", VIK_LAYER_WIDGET_SPINBUTTON
, param_scales
+ 1 },
64 static gchar
*dem_colors
[] = {
170 static const guint dem_n_colors
= sizeof(dem_colors
)/sizeof(dem_colors
[0]);
172 enum { PARAM_FILES
=0, PARAM_COLOR
, PARAM_MAX_ELEV
, PARAM_LINE_THICKNESS
, NUM_PARAMS
};
174 VikLayerInterface vik_dem_layer_interface
= {
188 (VikLayerFuncCreate
) vik_dem_layer_create
,
189 (VikLayerFuncRealize
) NULL
,
190 (VikLayerFuncPostRead
) dem_layer_post_read
,
191 (VikLayerFuncFree
) vik_dem_layer_free
,
193 (VikLayerFuncProperties
) NULL
,
194 (VikLayerFuncDraw
) vik_dem_layer_draw
,
195 (VikLayerFuncChangeCoordMode
) NULL
,
197 (VikLayerFuncSetMenuItemsSelection
) NULL
,
198 (VikLayerFuncGetMenuItemsSelection
) NULL
,
200 (VikLayerFuncAddMenuItems
) NULL
,
201 (VikLayerFuncSublayerAddMenuItems
) NULL
,
203 (VikLayerFuncSublayerRenameRequest
) NULL
,
204 (VikLayerFuncSublayerToggleVisible
) NULL
,
206 (VikLayerFuncCopy
) dem_layer_copy
,
207 (VikLayerFuncMarshall
) dem_layer_marshall
,
208 (VikLayerFuncUnmarshall
) dem_layer_unmarshall
,
210 (VikLayerFuncSetParam
) dem_layer_set_param
,
211 (VikLayerFuncGetParam
) dem_layer_get_param
,
213 (VikLayerFuncReadFileData
) NULL
,
214 (VikLayerFuncWriteFileData
) NULL
,
216 (VikLayerFuncDeleteItem
) NULL
,
217 (VikLayerFuncCopyItem
) NULL
,
218 (VikLayerFuncPasteItem
) NULL
,
219 (VikLayerFuncFreeCopiedItem
) NULL
,
220 (VikLayerFuncDragDropRequest
) NULL
,
223 struct _VikDEMLayer
{
229 guint8 line_thickness
;
233 GType
vik_dem_layer_get_type ()
235 static GType vdl_type
= 0;
239 static const GTypeInfo vdl_info
=
241 sizeof (VikDEMLayerClass
),
242 NULL
, /* base_init */
243 NULL
, /* base_finalize */
244 NULL
, /* class init */
245 NULL
, /* class_finalize */
246 NULL
, /* class_data */
247 sizeof (VikDEMLayer
),
249 NULL
/* instance init */
251 vdl_type
= g_type_register_static ( VIK_LAYER_TYPE
, "VikDEMLayer", &vdl_info
, 0 );
257 static VikDEMLayer
*dem_layer_copy ( VikDEMLayer
*vdl
, gpointer vp
)
259 VikDEMLayer
*rv
= vik_dem_layer_new ( );
262 /* TODO -- FIX for files */
264 rv
->color
= g_strdup ( vdl
->color
);
265 rv
->max_elev
= vdl
->max_elev
;
266 rv
->line_thickness
= vdl
->line_thickness
;
268 g_object_ref ( rv
->gc
);
272 static void dem_layer_marshall( VikDEMLayer
*vdl
, guint8
**data
, gint
*len
)
274 vik_layer_marshall_params ( VIK_LAYER(vdl
), data
, len
);
277 static VikDEMLayer
*dem_layer_unmarshall( guint8
*data
, gint len
, VikViewport
*vvp
)
279 VikDEMLayer
*rv
= vik_dem_layer_new ( vvp
);
280 vik_layer_unmarshall_params ( VIK_LAYER(rv
), data
, len
, vvp
);
284 gboolean
dem_layer_set_param ( VikDEMLayer
*vdl
, guint16 id
, VikLayerParamData data
, VikViewport
*vp
)
288 case PARAM_COLOR
: if ( vdl
->color
) g_free ( vdl
->color
); vdl
->color
= g_strdup ( data
.s
); break;
289 case PARAM_MAX_ELEV
: vdl
->max_elev
= data
.d
; break;
290 case PARAM_LINE_THICKNESS
: if ( data
.u
>= 1 && data
.u
<= 15 ) vdl
->line_thickness
= data
.u
; break;
291 case PARAM_FILES
: a_dems_load_list ( &(data
.sl
) ); a_dems_list_free ( vdl
->files
); vdl
->files
= data
.sl
; break;
296 static VikLayerParamData
dem_layer_get_param ( VikDEMLayer
*vdl
, guint16 id
)
298 VikLayerParamData rv
;
301 case PARAM_FILES
: rv
.sl
= vdl
->files
; break;
302 case PARAM_COLOR
: rv
.s
= vdl
->color
? vdl
->color
: ""; break;
303 case PARAM_MAX_ELEV
: rv
.d
= vdl
->max_elev
; break;
304 case PARAM_LINE_THICKNESS
: rv
.i
= vdl
->line_thickness
; break;
309 static void dem_layer_post_read ( VikDEMLayer
*vdl
, VikViewport
*vp
)
312 g_object_unref ( G_OBJECT(vdl
->gc
) );
314 vdl
->gc
= vik_viewport_new_gc ( vp
, vdl
->color
, vdl
->line_thickness
);
317 VikDEMLayer
*vik_dem_layer_new ( )
319 VikDEMLayer
*vdl
= VIK_DEM_LAYER ( g_object_new ( VIK_DEM_LAYER_TYPE
, NULL
) );
320 vik_layer_init ( VIK_LAYER(vdl
), VIK_LAYER_DEM
);
327 vdl
->gcs
= g_malloc(sizeof(GdkGC
*)*dem_n_colors
);
329 vdl
->max_elev
= 1000.0;
330 vdl
->line_thickness
= 3;
336 static void vik_dem_layer_draw_dem ( VikDEMLayer
*vdl
, VikViewport
*vp
, VikDEM
*dem
)
338 VikCoord tleft
, tright
, bleft
, bright
;
339 VikDEMColumn
*column
;
341 struct LatLon dem_northeast
, dem_southwest
;
342 gdouble max_lat
, max_lon
, min_lat
, min_lon
;
345 /**** Check if viewport and DEM data overlap ****/
347 /* get min, max lat/lon of viewport */
348 vik_viewport_screen_to_coord ( vp
, 0, 0, &tleft
);
349 vik_viewport_screen_to_coord ( vp
, vik_viewport_get_width(vp
), 0, &tright
);
350 vik_viewport_screen_to_coord ( vp
, 0, vik_viewport_get_height(vp
), &bleft
);
351 vik_viewport_screen_to_coord ( vp
, vik_viewport_get_width(vp
), vik_viewport_get_height(vp
), &bright
);
353 vik_coord_convert(&tleft
, VIK_COORD_LATLON
);
354 vik_coord_convert(&tright
, VIK_COORD_LATLON
);
355 vik_coord_convert(&bleft
, VIK_COORD_LATLON
);
356 vik_coord_convert(&bright
, VIK_COORD_LATLON
);
358 max_lat
= MAX(tleft
.north_south
, tright
.north_south
);
359 min_lat
= MIN(bleft
.north_south
, bright
.north_south
);
360 max_lon
= MAX(tright
.east_west
, bright
.east_west
);
361 min_lon
= MIN(tleft
.east_west
, bleft
.east_west
);
363 /* get min, max lat/lon of DEM data */
364 if ( dem
->horiz_units
== VIK_DEM_HORIZ_LL_ARCSECONDS
) {
365 dem_northeast
.lat
= dem
->max_north
/ 3600.0;
366 dem_northeast
.lon
= dem
->max_east
/ 3600.0;
367 dem_southwest
.lat
= dem
->min_north
/ 3600.0;
368 dem_southwest
.lon
= dem
->min_east
/ 3600.0;
369 } else if ( dem
->horiz_units
== VIK_DEM_HORIZ_UTM_METERS
) {
370 struct UTM dem_northeast_utm
, dem_southwest_utm
;
371 dem_northeast_utm
.northing
= dem
->max_north
;
372 dem_northeast_utm
.easting
= dem
->max_east
;
373 dem_southwest_utm
.northing
= dem
->min_north
;
374 dem_southwest_utm
.easting
= dem
->min_east
;
375 dem_northeast_utm
.zone
= dem_southwest_utm
.zone
= dem
->utm_zone
;
376 dem_northeast_utm
.letter
= dem_southwest_utm
.letter
= dem
->utm_letter
;
378 a_coords_utm_to_latlon(&dem_northeast_utm
, &dem_northeast
);
379 a_coords_utm_to_latlon(&dem_southwest_utm
, &dem_southwest
);
382 if ( (max_lat
> dem_northeast
.lat
&& min_lat
> dem_northeast
.lat
) ||
383 (max_lat
< dem_southwest
.lat
&& min_lat
< dem_southwest
.lat
) )
385 else if ( (max_lon
> dem_northeast
.lon
&& min_lon
> dem_northeast
.lon
) ||
386 (max_lon
< dem_southwest
.lon
&& min_lon
< dem_southwest
.lon
) )
388 /* else they overlap */
390 /**** End Overlap Check ****/
392 if ( dem
->horiz_units
== VIK_DEM_HORIZ_LL_ARCSECONDS
) {
393 VikCoord tmp
; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
395 gdouble max_lat_as
, max_lon_as
, min_lat_as
, min_lon_as
;
396 gdouble start_lat_as
, end_lat_as
, start_lon_as
, end_lon_as
;
398 gdouble start_lat
, end_lat
, start_lon
, end_lon
;
400 struct LatLon counter
;
402 guint x
, y
, start_x
, start_y
;
406 guint skip_factor
= ceil ( vik_viewport_get_xmpp(vp
) / 40 ); /* todo: smarter calculation. */
408 gdouble nscale_deg
= dem
->north_scale
/ ((gdouble
) 3600);
409 gdouble escale_deg
= dem
->east_scale
/ ((gdouble
) 3600);
411 max_lat_as
= max_lat
* 3600;
412 min_lat_as
= min_lat
* 3600;
413 max_lon_as
= max_lon
* 3600;
414 min_lon_as
= min_lon
* 3600;
416 start_lat_as
= MAX(min_lat_as
, dem
->min_north
);
417 end_lat_as
= MIN(max_lat_as
, dem
->max_north
);
418 start_lon_as
= MAX(min_lon_as
, dem
->min_east
);
419 end_lon_as
= MIN(max_lon_as
, dem
->max_east
);
421 start_lat
= floor(start_lat_as
/ dem
->north_scale
) * nscale_deg
;
422 end_lat
= ceil (end_lat_as
/ dem
->north_scale
) * nscale_deg
;
423 start_lon
= floor(start_lon_as
/ dem
->east_scale
) * escale_deg
;
424 end_lon
= ceil (end_lon_as
/ dem
->east_scale
) * escale_deg
;
426 vik_dem_east_north_to_xy ( dem
, start_lon_as
, start_lat_as
, &start_x
, &start_y
);
428 for ( x
=start_x
, counter
.lon
= start_lon
; counter
.lon
<= end_lon
; counter
.lon
+= escale_deg
* skip_factor
, x
+= skip_factor
) {
429 if ( x
> 0 && x
< dem
->n_columns
) {
430 column
= g_ptr_array_index ( dem
->columns
, x
);
431 for ( y
=start_y
, counter
.lat
= start_lat
; counter
.lat
<= end_lat
; counter
.lat
+= nscale_deg
* skip_factor
, y
+= skip_factor
) {
432 if ( y
> column
->n_points
)
434 elev
= column
->points
[y
];
435 if ( elev
> vdl
->max_elev
) elev
=vdl
->max_elev
;
439 vik_coord_load_from_latlon(&tmp
, vik_viewport_get_coord_mode(vp
), &counter
);
440 vik_viewport_coord_to_screen(vp
, &tmp
, &a
, &b
);
441 if ( elev
== VIK_DEM_INVALID_ELEVATION
)
442 ; /* don't draw it */
443 else if ( elev
<= 0 )
444 vik_viewport_draw_rectangle(vp
, vdl
->gcs
[0], TRUE
, a
-2, b
-2, 4, 4 );
446 vik_viewport_draw_rectangle(vp
, vdl
->gcs
[(gint
)floor(elev
/vdl
->max_elev
*(dem_n_colors
-2))+1], TRUE
, a
-2, b
-2, 4, 4 );
451 } else if ( dem
->horiz_units
== VIK_DEM_HORIZ_UTM_METERS
) {
452 gdouble max_nor
, max_eas
, min_nor
, min_eas
;
453 gdouble start_nor
, start_eas
, end_nor
, end_eas
;
457 guint x
, y
, start_x
, start_y
;
459 VikCoord tmp
; /* TODO: don't use coord_load_from_latlon, especially if in latlon drawing mode */
462 guint skip_factor
= ceil ( vik_viewport_get_xmpp(vp
) / 10 ); /* todo: smarter calculation. */
464 vik_coord_convert(&tleft
, VIK_COORD_UTM
);
465 vik_coord_convert(&tright
, VIK_COORD_UTM
);
466 vik_coord_convert(&bleft
, VIK_COORD_UTM
);
467 vik_coord_convert(&bright
, VIK_COORD_UTM
);
469 max_nor
= MAX(tleft
.north_south
, tright
.north_south
);
470 min_nor
= MIN(bleft
.north_south
, bright
.north_south
);
471 max_eas
= MAX(bright
.east_west
, tright
.east_west
);
472 min_eas
= MIN(bleft
.east_west
, tleft
.east_west
);
474 start_nor
= MAX(min_nor
, dem
->min_north
);
475 end_nor
= MIN(max_nor
, dem
->max_north
);
476 if ( tleft
.utm_zone
== dem
->utm_zone
&& bleft
.utm_zone
== dem
->utm_zone
477 && (tleft
.utm_letter
>= 'N') == (dem
->utm_letter
>= 'N')
478 && (bleft
.utm_letter
>= 'N') == (dem
->utm_letter
>= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
479 start_eas
= MAX(min_eas
, dem
->min_east
);
481 start_eas
= dem
->min_east
;
482 if ( tright
.utm_zone
== dem
->utm_zone
&& bright
.utm_zone
== dem
->utm_zone
483 && (tright
.utm_letter
>= 'N') == (dem
->utm_letter
>= 'N')
484 && (bright
.utm_letter
>= 'N') == (dem
->utm_letter
>= 'N') ) /* if the utm zones/hemispheres are different, min_eas will be bogus */
485 end_eas
= MIN(max_eas
, dem
->max_east
);
487 end_eas
= dem
->max_east
;
489 start_nor
= floor(start_nor
/ dem
->north_scale
) * dem
->north_scale
;
490 end_nor
= ceil (end_nor
/ dem
->north_scale
) * dem
->north_scale
;
491 start_eas
= floor(start_eas
/ dem
->east_scale
) * dem
->east_scale
;
492 end_eas
= ceil (end_eas
/ dem
->east_scale
) * dem
->east_scale
;
494 vik_dem_east_north_to_xy ( dem
, start_eas
, start_nor
, &start_x
, &start_y
);
496 /* TODO: why start_x and start_y are -1 -- rounding error from above? */
498 counter
.zone
= dem
->utm_zone
;
499 counter
.letter
= dem
->utm_letter
;
501 for ( x
=start_x
, counter
.easting
= start_eas
; counter
.easting
<= end_eas
; counter
.easting
+= dem
->east_scale
* skip_factor
, x
+= skip_factor
) {
502 if ( x
> 0 && x
< dem
->n_columns
) {
503 column
= g_ptr_array_index ( dem
->columns
, x
);
504 for ( y
=start_y
, counter
.northing
= start_nor
; counter
.northing
<= end_nor
; counter
.northing
+= dem
->north_scale
* skip_factor
, y
+= skip_factor
) {
505 if ( y
> column
->n_points
)
507 elev
= column
->points
[y
];
508 if ( elev
> vdl
->max_elev
) elev
=vdl
->max_elev
;
511 vik_coord_load_from_utm(&tmp
, vik_viewport_get_coord_mode(vp
), &counter
);
512 vik_viewport_coord_to_screen(vp
, &tmp
, &a
, &b
);
513 if ( elev
== VIK_DEM_INVALID_ELEVATION
)
514 ; /* don't draw it */
515 else if ( elev
<= 0 )
516 vik_viewport_draw_rectangle(vp
, vdl
->gcs
[0], TRUE
, a
-2, b
-2, 4, 4 );
518 vik_viewport_draw_rectangle(vp
, vdl
->gcs
[(gint
)floor(elev
/vdl
->max_elev
*(dem_n_colors
-2))+1], TRUE
, a
-2, b
-2, 4, 4 );
526 void vik_dem_layer_draw ( VikDEMLayer
*vdl
, gpointer data
)
528 VikViewport
*vp
= (VikViewport
*) data
;
529 GList
*dems_iter
= vdl
->files
;
531 while ( dems_iter
) {
532 dem
= a_dems_get ( (const char *) (dems_iter
->data
) );
534 vik_dem_layer_draw_dem ( vdl
, vp
, dem
);
535 dems_iter
= dems_iter
->next
;
539 void vik_dem_layer_free ( VikDEMLayer
*vdl
)
541 if ( vdl
->gc
!= NULL
)
542 g_object_unref ( G_OBJECT(vdl
->gc
) );
544 if ( vdl
->color
!= NULL
)
545 g_free ( vdl
->color
);
547 a_dems_list_free ( vdl
->files
);
550 static void dem_layer_update_gc ( VikDEMLayer
*vdl
, VikViewport
*vp
, const gchar
*color
)
555 g_free ( vdl
->color
);
557 vdl
->color
= g_strdup ( color
);
560 g_object_unref ( G_OBJECT(vdl
->gc
) );
562 vdl
->gc
= vik_viewport_new_gc ( vp
, vdl
->color
, vdl
->line_thickness
);
564 for ( i
= 0 ; i
< dem_n_colors
; i
++ )
565 vdl
->gcs
[i
] = vik_viewport_new_gc ( vp
, dem_colors
[i
], vdl
->line_thickness
);
569 VikDEMLayer
*vik_dem_layer_create ( VikViewport
*vp
)
571 VikDEMLayer
*vdl
= vik_dem_layer_new ();
572 dem_layer_update_gc ( vdl
, vp
, "red" );