]> git.street.me.uk Git - andy/viking.git/blob - src/vikcoordlayer.c
ee4bec2a75a741ab9a3ad169af5c962efee0a84c
[andy/viking.git] / src / vikcoordlayer.c
1 /*
2  * viking -- GPS Data and Topo Analyzer, Explorer, and Manager
3  *
4  * Copyright (C) 2003-2005, Evan Battaglia <gtoevan@gmx.net>
5  *
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.
10  *
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.
15  *
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
19  *
20  */
21 #ifdef HAVE_CONFIG_H
22 #include "config.h"
23 #endif
24
25 #ifdef HAVE_MATH_H
26 #include <math.h>
27 #endif
28 #include <glib/gi18n.h>
29
30 #include "viking.h"
31 #include "icons/icons.h"
32
33 static VikCoordLayer *coord_layer_new ( VikViewport *vp );
34 static void coord_layer_draw ( VikCoordLayer *vcl, VikViewport *vp );
35 static void coord_layer_free ( VikCoordLayer *vcl );
36 static VikCoordLayer *coord_layer_create ( VikViewport *vp );
37 static void coord_layer_marshall( VikCoordLayer *vcl, guint8 **data, gint *len );
38 static VikCoordLayer *coord_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp );
39 static gboolean coord_layer_set_param ( VikCoordLayer *vcl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation );
40 static VikLayerParamData coord_layer_get_param ( VikCoordLayer *vcl, guint16 id, gboolean is_file_operation );
41 static void coord_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file );
42 static void coord_layer_update_gc ( VikCoordLayer *vcl, VikViewport *vp );
43
44 static VikLayerParamScale param_scales[] = {
45   { 0.05, 60.0, 0.25, 10 },
46   { 1, 10, 1, 0 },
47 };
48
49 static VikLayerParamData color_default ( void ) {
50   VikLayerParamData data; gdk_color_parse ( "red", &data.c ); return data;
51   // or: return VIK_LPD_COLOR ( 0, 65535, 0, 0 );
52 }
53 static VikLayerParamData min_inc_default ( void ) { return VIK_LPD_DOUBLE ( 1.0 ); }
54 static VikLayerParamData line_thickness_default ( void ) { return VIK_LPD_UINT ( 3 ); }
55
56 static VikLayerParam coord_layer_params[] = {
57   { VIK_LAYER_COORD, "color", VIK_LAYER_PARAM_COLOR, VIK_LAYER_GROUP_NONE, N_("Color:"), VIK_LAYER_WIDGET_COLOR, NULL, NULL, NULL, color_default, NULL, NULL },
58   { VIK_LAYER_COORD, "min_inc", VIK_LAYER_PARAM_DOUBLE, VIK_LAYER_GROUP_NONE, N_("Minutes Width:"), VIK_LAYER_WIDGET_SPINBUTTON, &param_scales[0], NULL, NULL, min_inc_default, NULL, NULL },
59   { VIK_LAYER_COORD, "line_thickness", VIK_LAYER_PARAM_UINT, VIK_LAYER_GROUP_NONE, N_("Line Thickness:"), VIK_LAYER_WIDGET_SPINBUTTON, &param_scales[1], NULL, NULL, line_thickness_default, NULL, NULL },
60 };
61
62 enum { PARAM_COLOR = 0, PARAM_MIN_INC, PARAM_LINE_THICKNESS, NUM_PARAMS };
63
64 VikLayerInterface vik_coord_layer_interface = {
65   "Coord",
66   N_("Coordinate"),
67   NULL,
68   &vikcoordlayer_pixbuf,
69
70   NULL,
71   0,
72
73   coord_layer_params,
74   NUM_PARAMS,
75   NULL,
76   0,
77
78   VIK_MENU_ITEM_ALL,
79
80   (VikLayerFuncCreate)                  coord_layer_create,
81   (VikLayerFuncRealize)                 NULL,
82   (VikLayerFuncPostRead)                coord_layer_post_read,
83   (VikLayerFuncFree)                    coord_layer_free,
84
85   (VikLayerFuncProperties)              NULL,
86   (VikLayerFuncDraw)                    coord_layer_draw,
87   (VikLayerFuncChangeCoordMode)         NULL,
88
89   (VikLayerFuncGetTimestamp)            NULL,
90
91   (VikLayerFuncSetMenuItemsSelection)   NULL,
92   (VikLayerFuncGetMenuItemsSelection)   NULL,
93
94   (VikLayerFuncAddMenuItems)            NULL,
95   (VikLayerFuncSublayerAddMenuItems)    NULL,
96
97   (VikLayerFuncSublayerRenameRequest)   NULL,
98   (VikLayerFuncSublayerToggleVisible)   NULL,
99   (VikLayerFuncSublayerTooltip)         NULL,
100   (VikLayerFuncLayerTooltip)            NULL,
101   (VikLayerFuncLayerSelected)           NULL,
102
103   (VikLayerFuncMarshall)                coord_layer_marshall,
104   (VikLayerFuncUnmarshall)              coord_layer_unmarshall,
105
106   (VikLayerFuncSetParam)                coord_layer_set_param,
107   (VikLayerFuncGetParam)                coord_layer_get_param,
108   (VikLayerFuncChangeParam)             NULL,
109
110   (VikLayerFuncReadFileData)            NULL,
111   (VikLayerFuncWriteFileData)           NULL,
112
113   (VikLayerFuncDeleteItem)              NULL,
114   (VikLayerFuncCutItem)                 NULL,
115   (VikLayerFuncCopyItem)                NULL,
116   (VikLayerFuncPasteItem)               NULL,
117   (VikLayerFuncFreeCopiedItem)          NULL,
118   (VikLayerFuncDragDropRequest)         NULL,
119
120   (VikLayerFuncSelectClick)             NULL,
121   (VikLayerFuncSelectMove)              NULL,
122   (VikLayerFuncSelectRelease)           NULL,
123   (VikLayerFuncSelectedViewportMenu)    NULL,
124 };
125
126 struct _VikCoordLayer {
127   VikLayer vl;
128   GdkGC *gc;
129   gdouble deg_inc;
130   guint8 line_thickness;
131   GdkColor color;
132 };
133
134 GType vik_coord_layer_get_type ()
135 {
136   static GType vcl_type = 0;
137
138   if (!vcl_type)
139   {
140     static const GTypeInfo vcl_info =
141     {
142       sizeof (VikCoordLayerClass),
143       NULL, /* base_init */
144       NULL, /* base_finalize */
145       NULL, /* class init */
146       NULL, /* class_finalize */
147       NULL, /* class_data */
148       sizeof (VikCoordLayer),
149       0,
150       NULL /* instance init */
151     };
152     vcl_type = g_type_register_static ( VIK_LAYER_TYPE, "VikCoordLayer", &vcl_info, 0 );
153   }
154
155   return vcl_type;
156 }
157
158 static void coord_layer_marshall( VikCoordLayer *vcl, guint8 **data, gint *len )
159 {
160   vik_layer_marshall_params ( VIK_LAYER(vcl), data, len );
161 }
162
163 static VikCoordLayer *coord_layer_unmarshall( guint8 *data, gint len, VikViewport *vvp )
164 {
165   VikCoordLayer *rv = coord_layer_new ( vvp );
166   vik_layer_unmarshall_params ( VIK_LAYER(rv), data, len, vvp );
167   coord_layer_update_gc ( rv, vvp );
168   return rv;
169 }
170
171 // NB VikViewport can be null as it's not used ATM
172 gboolean coord_layer_set_param ( VikCoordLayer *vcl, guint16 id, VikLayerParamData data, VikViewport *vp, gboolean is_file_operation )
173 {
174   switch ( id )
175   {
176     case PARAM_COLOR: vcl->color = data.c; break;
177     case PARAM_MIN_INC: vcl->deg_inc = data.d / 60.0; break;
178     case PARAM_LINE_THICKNESS: if ( data.u >= 1 && data.u <= 15 ) vcl->line_thickness = data.u; break;
179     default: break;
180   }
181   return TRUE;
182 }
183
184 static VikLayerParamData coord_layer_get_param ( VikCoordLayer *vcl, guint16 id, gboolean is_file_operation )
185 {
186   VikLayerParamData rv;
187   switch ( id )
188   {
189     case PARAM_COLOR: rv.c = vcl->color; break;
190     case PARAM_MIN_INC: rv.d = vcl->deg_inc * 60.0; break;
191     case PARAM_LINE_THICKNESS: rv.i = vcl->line_thickness; break;
192     default: break;
193   }
194   return rv;
195 }
196
197 static void coord_layer_post_read ( VikLayer *vl, VikViewport *vp, gboolean from_file )
198 {
199   VikCoordLayer *vcl = VIK_COORD_LAYER(vl);
200   if ( vcl->gc )
201     g_object_unref ( G_OBJECT(vcl->gc) );
202
203   vcl->gc = vik_viewport_new_gc_from_color ( vp, &(vcl->color), vcl->line_thickness );
204 }
205
206 static VikCoordLayer *coord_layer_new ( VikViewport *vvp )
207 {
208   VikCoordLayer *vcl = VIK_COORD_LAYER ( g_object_new ( VIK_COORD_LAYER_TYPE, NULL ) );
209   vik_layer_set_type ( VIK_LAYER(vcl), VIK_LAYER_COORD );
210
211   vik_layer_set_defaults ( VIK_LAYER(vcl), vvp );
212
213   vcl->gc = NULL;
214
215   return vcl;
216 }
217
218 static void coord_layer_draw ( VikCoordLayer *vcl, VikViewport *vp )
219 {
220   if ( !vcl->gc ) {
221     return;
222   }
223
224   if ( vik_viewport_get_coord_mode(vp) != VIK_COORD_UTM ) 
225   {
226     VikCoord left, right, left2, right2;
227     gdouble l, r, i, j;
228     gint x1, y1, x2, y2, smod = 1, mmod = 1;
229     gboolean mins = FALSE, secs = FALSE;
230     GdkGC *dgc = vik_viewport_new_gc_from_color(vp, &(vcl->color), vcl->line_thickness);
231     GdkGC *mgc = vik_viewport_new_gc_from_color(vp, &(vcl->color), MAX(vcl->line_thickness/2, 1));
232     GdkGC *sgc = vik_viewport_new_gc_from_color(vp, &(vcl->color), MAX(vcl->line_thickness/5, 1));
233
234     vik_viewport_screen_to_coord ( vp, 0, 0, &left );
235     vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), 0, &right );
236     vik_viewport_screen_to_coord ( vp, 0, vik_viewport_get_height(vp), &left2 );
237     vik_viewport_screen_to_coord ( vp, vik_viewport_get_width(vp), vik_viewport_get_height(vp), &right2 );
238
239 #define CLINE(gc, c1, c2) { \
240           vik_viewport_coord_to_screen(vp, (c1), &x1, &y1);  \
241           vik_viewport_coord_to_screen(vp, (c2), &x2, &y2);  \
242           vik_viewport_draw_line (vp, (gc), x1, y1, x2, y2); \
243         }
244
245     l = left.east_west;
246     r = right.east_west;
247     if (60*fabs(l-r) < 4) {
248       secs = TRUE;
249       smod = MIN(6, (int)ceil(3600*fabs(l-r)/30.0));
250     }
251     if (fabs(l-r) < 4) {
252       mins = TRUE;
253       mmod = MIN(6, (int)ceil(60*fabs(l-r)/30.0));
254     }
255     for (i=floor(l*60); i<ceil(r*60); i+=1.0) {
256       if (secs) {
257         for (j=i*60+1; j<(i+1)*60; j+=1.0) {
258           left.east_west = j/3600.0;
259           left2.east_west = j/3600.0;
260           if ((int)j % smod == 0) CLINE(sgc, &left, &left2);
261         }
262       }
263       if (mins) {
264         left.east_west = i/60.0;
265         left2.east_west = i/60.0;
266         if ((int)i % mmod == 0) CLINE(mgc, &left, &left2);
267       }
268       if ((int)i % 60 == 0) {
269         left.east_west = i/60.0;
270         left2.east_west = i/60.0;
271         CLINE(dgc, &left, &left2);
272       }
273     }
274
275     vik_viewport_screen_to_coord ( vp, 0, 0, &left );
276     l = left2.north_south;
277     r = left.north_south;
278     for (i=floor(l*60); i<ceil(r*60); i+=1.0) {
279       if (secs) {
280         for (j=i*60+1; j<(i+1)*60; j+=1.0) {
281           left.north_south = j/3600.0;
282           right.north_south = j/3600.0;
283           if ((int)j % smod == 0) CLINE(sgc, &left, &right);
284         }
285       }
286       if (mins) {
287         left.north_south = i/60.0;
288         right.north_south = i/60.0;
289         if ((int)i % mmod == 0) CLINE(mgc, &left, &right);
290       }
291       if ((int)i % 60 == 0) {
292         left.north_south = i/60.0;
293         right.north_south = i/60.0;
294         CLINE(dgc, &left, &right);
295       }
296     }
297 #undef CLINE
298     g_object_unref(dgc);
299     g_object_unref(sgc);
300     g_object_unref(mgc);
301     return;
302   }
303
304   if ( vik_viewport_get_coord_mode(vp) == VIK_COORD_UTM ) 
305   {
306     const struct UTM *center = (const struct UTM *)vik_viewport_get_center ( vp );
307     gdouble xmpp = vik_viewport_get_xmpp ( vp ), ympp = vik_viewport_get_ympp ( vp );
308     guint16 width = vik_viewport_get_width ( vp ), height = vik_viewport_get_height ( vp );
309     struct LatLon ll, ll2, min, max;
310     double lon;
311     int x1, x2;
312     struct UTM utm;
313
314     utm = *center;
315     utm.northing = center->northing - ( ympp * height / 2 );
316
317     a_coords_utm_to_latlon ( &utm, &ll );
318
319     utm.northing = center->northing + ( ympp * height / 2 );
320
321     a_coords_utm_to_latlon ( &utm, &ll2 );
322
323     {
324       /* find corner coords in lat/lon.
325         start at whichever is less: top or bottom left lon. goto whichever more: top or bottom right lon
326       */
327       struct LatLon topleft, topright, bottomleft, bottomright;
328       struct UTM temp_utm;
329       temp_utm = *center;
330       temp_utm.easting -= (width/2)*xmpp;
331       temp_utm.northing += (height/2)*ympp;
332       a_coords_utm_to_latlon ( &temp_utm, &topleft );
333       temp_utm.easting += (width*xmpp);
334       a_coords_utm_to_latlon ( &temp_utm, &topright );
335       temp_utm.northing -= (height*ympp);
336       a_coords_utm_to_latlon ( &temp_utm, &bottomright );
337       temp_utm.easting -= (width*xmpp);
338       a_coords_utm_to_latlon ( &temp_utm, &bottomleft );
339       min.lon = (topleft.lon < bottomleft.lon) ? topleft.lon : bottomleft.lon;
340       max.lon = (topright.lon > bottomright.lon) ? topright.lon : bottomright.lon;
341       min.lat = (bottomleft.lat < bottomright.lat) ? bottomleft.lat : bottomright.lat;
342       max.lat = (topleft.lat > topright.lat) ? topleft.lat : topright.lat;
343     }
344
345     /* Can zoom out more than whole world and so the above can give invalid positions */
346     /* Restrict values properly so drawing doesn't go into a near 'infinite' loop */
347     if ( min.lon < -180.0 )
348       min.lon = -180.0;
349     if ( max.lon > 180.0 )
350       max.lon = 180.0;
351     if ( min.lat < -90.0 )
352       min.lat = -90.0;
353     if ( max.lat > 90.0 )
354       max.lat = 90.0;
355
356     lon = ((double) ((long) ((min.lon)/ vcl->deg_inc))) * vcl->deg_inc;
357     ll.lon = ll2.lon = lon;
358
359     for (; ll.lon <= max.lon; ll.lon+=vcl->deg_inc, ll2.lon+=vcl->deg_inc )
360     {
361       a_coords_latlon_to_utm ( &ll, &utm );
362       x1 = ( (utm.easting - center->easting) / xmpp ) + (width / 2);
363       a_coords_latlon_to_utm ( &ll2, &utm );
364       x2 = ( (utm.easting - center->easting) / xmpp ) + (width / 2);
365       vik_viewport_draw_line (vp, vcl->gc, x1, height, x2, 0);
366     }
367
368     utm = *center;
369     utm.easting = center->easting - ( xmpp * width / 2 );
370
371     a_coords_utm_to_latlon ( &utm, &ll );
372
373     utm.easting = center->easting + ( xmpp * width / 2 );
374
375     a_coords_utm_to_latlon ( &utm, &ll2 );
376
377     /* really lat, just reusing a variable */
378     lon = ((double) ((long) ((min.lat)/ vcl->deg_inc))) * vcl->deg_inc;
379     ll.lat = ll2.lat = lon;
380
381     for (; ll.lat <= max.lat ; ll.lat+=vcl->deg_inc, ll2.lat+=vcl->deg_inc )
382     {
383       a_coords_latlon_to_utm ( &ll, &utm );
384       x1 = (height / 2) - ( (utm.northing - center->northing) / ympp );
385       a_coords_latlon_to_utm ( &ll2, &utm );
386       x2 = (height / 2) - ( (utm.northing - center->northing) / ympp );
387       vik_viewport_draw_line (vp, vcl->gc, width, x2, 0, x1);
388     }
389   }
390 }
391
392 static void coord_layer_free ( VikCoordLayer *vcl )
393 {
394   if ( vcl->gc != NULL )
395     g_object_unref ( G_OBJECT(vcl->gc) );
396 }
397
398 static void coord_layer_update_gc ( VikCoordLayer *vcl, VikViewport *vp )
399 {
400   if ( vcl->gc )
401     g_object_unref ( G_OBJECT(vcl->gc) );
402
403   vcl->gc = vik_viewport_new_gc_from_color ( vp, &(vcl->color), vcl->line_thickness );
404 }
405
406 static VikCoordLayer *coord_layer_create ( VikViewport *vp )
407 {
408   VikCoordLayer *vcl = coord_layer_new ( vp );
409   if ( vp )
410     coord_layer_update_gc ( vcl, vp );
411   return vcl;
412 }