]> Pileus Git - grits/blob - src/gis-viewer.c
Add support for GisMarker to Gis{Viewer,OpenGL}
[grits] / src / gis-viewer.c
1 /*
2  * Copyright (C) 2009 Andy Spencer <spenceal@rose-hulman.edu>
3  *
4  * This program is free software: you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
16  */
17
18 #include <config.h>
19 #include <gtk/gtk.h>
20 #include <gdk/gdkkeysyms.h>
21
22 #include "gis-marshal.h"
23 #include "gis-viewer.h"
24
25 #include "gis-util.h"
26
27
28 /* Constants */
29 enum {
30         PROP_0,
31         PROP_TIME,
32         PROP_SITE,
33         PROP_OFFLINE,
34 };
35 enum {
36         SIG_TIME_CHANGED,
37         SIG_SITE_CHANGED,
38         SIG_LOCATION_CHANGED,
39         SIG_ROTATION_CHANGED,
40         SIG_REFRESH,
41         SIG_OFFLINE,
42         NUM_SIGNALS,
43 };
44 static guint signals[NUM_SIGNALS];
45
46
47 /***********
48  * Helpers *
49  ***********/
50 /* Misc helpers */
51 static void _gis_viewer_fix_location(GisViewer *self)
52 {
53         while (self->location[0] <  -90) self->location[0] += 180;
54         while (self->location[0] >   90) self->location[0] -= 180;
55         while (self->location[1] < -180) self->location[1] += 360;
56         while (self->location[1] >  180) self->location[1] -= 360;
57         self->location[2] = ABS(self->location[2]);
58 }
59
60 /* Signal helpers */
61 static void _gis_viewer_emit_location_changed(GisViewer *self)
62 {
63         g_signal_emit(self, signals[SIG_LOCATION_CHANGED], 0,
64                         self->location[0],
65                         self->location[1],
66                         self->location[2]);
67 }
68 static void _gis_viewer_emit_rotation_changed(GisViewer *self)
69 {
70         g_signal_emit(self, signals[SIG_ROTATION_CHANGED], 0,
71                         self->rotation[0],
72                         self->rotation[1],
73                         self->rotation[2]);
74 }
75 static void _gis_viewer_emit_time_changed(GisViewer *self)
76 {
77         g_signal_emit(self, signals[SIG_TIME_CHANGED], 0,
78                         self->time);
79 }
80 static void _gis_viewer_emit_site_changed(GisViewer *self)
81 {
82         g_signal_emit(self, signals[SIG_SITE_CHANGED], 0,
83                         self->site);
84 }
85 static void _gis_viewer_emit_refresh(GisViewer *self)
86 {
87         g_signal_emit(self, signals[SIG_REFRESH], 0);
88 }
89 static void _gis_viewer_emit_offline(GisViewer *self)
90 {
91         g_signal_emit(self, signals[SIG_OFFLINE], 0,
92                         self->offline);
93 }
94
95 /*************
96  * Callbacks *
97  *************/
98 static gboolean on_button_press(GisViewer *self, GdkEventButton *event, gpointer _)
99 {
100         g_debug("GisViewer: on_button_press - Grabbing focus");
101         gtk_widget_grab_focus(GTK_WIDGET(self));
102         return TRUE;
103 }
104 static gboolean on_key_press(GisViewer *self, GdkEventKey *event, gpointer _)
105 {
106         g_debug("GisViewer: on_key_press - key=%x, state=%x, plus=%x",
107                         event->keyval, event->state, GDK_plus);
108
109         double lat, lon, elev, pan;
110         gis_viewer_get_location(self, &lat, &lon, &elev);
111         pan = MIN(elev/(EARTH_R/2), 30);
112         guint kv = event->keyval;
113         gdk_threads_leave();
114         if      (kv == GDK_Left  || kv == GDK_h) gis_viewer_pan(self,  0,  -pan, 0);
115         else if (kv == GDK_Down  || kv == GDK_j) gis_viewer_pan(self, -pan, 0,   0);
116         else if (kv == GDK_Up    || kv == GDK_k) gis_viewer_pan(self,  pan, 0,   0);
117         else if (kv == GDK_Right || kv == GDK_l) gis_viewer_pan(self,  0,   pan, 0);
118         else if (kv == GDK_minus || kv == GDK_o) gis_viewer_zoom(self, 10./9);
119         else if (kv == GDK_plus  || kv == GDK_i) gis_viewer_zoom(self, 9./10);
120         else if (kv == GDK_H) gis_viewer_rotate(self,  0, 0, -2);
121         else if (kv == GDK_J) gis_viewer_rotate(self,  2, 0,  0);
122         else if (kv == GDK_K) gis_viewer_rotate(self, -2, 0,  0);
123         else if (kv == GDK_L) gis_viewer_rotate(self,  0, 0,  2);
124         return FALSE;
125 }
126 static void on_view_changed(GisViewer *self,
127                 gdouble _1, gdouble _2, gdouble _3)
128 {
129         gtk_widget_queue_draw(GTK_WIDGET(self));
130 }
131
132 /***********
133  * Methods *
134  ***********/
135 void gis_viewer_set_time(GisViewer *self, const char *time)
136 {
137         g_assert(GIS_IS_VIEWER(self));
138         g_debug("GisViewer: set_time - time=%s", time);
139         g_free(self->time);
140         self->time = g_strdup(time);
141         _gis_viewer_emit_time_changed(self);
142 }
143
144 gchar *gis_viewer_get_time(GisViewer *self)
145 {
146         g_assert(GIS_IS_VIEWER(self));
147         g_debug("GisViewer: get_time");
148         return self->time;
149 }
150
151 void gis_viewer_set_location(GisViewer *self, gdouble lat, gdouble lon, gdouble elev)
152 {
153         g_assert(GIS_IS_VIEWER(self));
154         g_debug("GisViewer: set_location");
155         self->location[0] = lat;
156         self->location[1] = lon;
157         self->location[2] = elev;
158         _gis_viewer_fix_location(self);
159         _gis_viewer_emit_location_changed(self);
160 }
161
162 void gis_viewer_get_location(GisViewer *self, gdouble *lat, gdouble *lon, gdouble *elev)
163 {
164         g_assert(GIS_IS_VIEWER(self));
165         //g_debug("GisViewer: get_location");
166         *lat  = self->location[0];
167         *lon  = self->location[1];
168         *elev = self->location[2];
169 }
170
171 void gis_viewer_pan(GisViewer *self, gdouble lat, gdouble lon, gdouble elev)
172 {
173         g_assert(GIS_IS_VIEWER(self));
174         g_debug("GisViewer: pan - lat=%8.3f, lon=%8.3f, elev=%8.3f", lat, lon, elev);
175         self->location[0] += lat;
176         self->location[1] += lon;
177         self->location[2] += elev;
178         _gis_viewer_fix_location(self);
179         _gis_viewer_emit_location_changed(self);
180 }
181
182 void gis_viewer_zoom(GisViewer *self, gdouble scale)
183 {
184         g_assert(GIS_IS_VIEWER(self));
185         g_debug("GisViewer: zoom");
186         self->location[2] *= scale;
187         _gis_viewer_emit_location_changed(self);
188 }
189
190 void gis_viewer_set_rotation(GisViewer *self, gdouble x, gdouble y, gdouble z)
191 {
192         g_assert(GIS_IS_VIEWER(self));
193         g_debug("GisViewer: set_rotation");
194         self->rotation[0] = x;
195         self->rotation[1] = y;
196         self->rotation[2] = z;
197         _gis_viewer_emit_rotation_changed(self);
198 }
199
200 void gis_viewer_get_rotation(GisViewer *self, gdouble *x, gdouble *y, gdouble *z)
201 {
202         g_assert(GIS_IS_VIEWER(self));
203         g_debug("GisViewer: get_rotation");
204         *x = self->rotation[0];
205         *y = self->rotation[1];
206         *z = self->rotation[2];
207 }
208
209 void gis_viewer_rotate(GisViewer *self, gdouble x, gdouble y, gdouble z)
210 {
211         g_assert(GIS_IS_VIEWER(self));
212         g_debug("GisViewer: rotate - x=%.0f, y=%.0f, z=%.0f", x, y, z);
213         self->rotation[0] += x;
214         self->rotation[1] += y;
215         self->rotation[2] += z;
216         _gis_viewer_emit_rotation_changed(self);
217 }
218
219 /* To be deprecated, use {get,set}_location */
220 void gis_viewer_set_site(GisViewer *self, const gchar *site)
221 {
222         g_assert(GIS_IS_VIEWER(self));
223         g_debug("GisViewer: set_site");
224         g_free(self->site);
225         self->site = g_strdup(site);
226         _gis_viewer_emit_site_changed(self);
227 }
228
229 gchar *gis_viewer_get_site(GisViewer *self)
230 {
231         g_assert(GIS_IS_VIEWER(self));
232         g_debug("GisViewer: get_site - %s", self->site);
233         return self->site;
234 }
235
236 void gis_viewer_refresh(GisViewer *self)
237 {
238         g_debug("GisViewer: refresh");
239         _gis_viewer_emit_refresh(self);
240 }
241
242 void gis_viewer_set_offline(GisViewer *self, gboolean offline)
243 {
244         g_assert(GIS_IS_VIEWER(self));
245         g_debug("GisViewer: set_offline - %d", offline);
246         self->offline = offline;
247         _gis_viewer_emit_offline(self);
248 }
249
250 gboolean gis_viewer_get_offline(GisViewer *self)
251 {
252         g_assert(GIS_IS_VIEWER(self));
253         g_debug("GisViewer: get_offline - %d", self->offline);
254         return self->offline;
255 }
256
257 void gis_viewer_add_object(GisViewer *self, GisObject *object)
258 {
259         g_debug("GisViewer: add_object - %d, %p", object->type, object);
260         self->objects = g_list_prepend(self->objects, object);
261 }
262
263 void gis_viewer_remove_object(GisViewer *self, GisObject *object)
264 {
265         g_debug("GisViewer: remove_object - %d, %p", object->type, object);
266         self->objects = g_list_remove(self->objects, object);
267 }
268
269 /* To be implemented by subclasses */
270 void gis_viewer_center_position(GisViewer *self,
271                 gdouble lat, gdouble lon, gdouble elev)
272 {
273         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
274         if (!klass->center_position)
275                 g_warning("GisViewer: center_position - Unimplemented");
276         klass->center_position(self, lat, lon, elev);
277 }
278
279 void gis_viewer_project(GisViewer *self,
280                 gdouble lat, gdouble lon, gdouble elev,
281                 gdouble *px, gdouble *py, gdouble *pz)
282 {
283         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
284         if (!klass->project)
285                 g_warning("GisViewer: project - Unimplemented");
286         klass->project(self, lat, lon, elev, px, py, pz);
287 }
288
289 void gis_viewer_clear_height_func(GisViewer *self)
290 {
291         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
292         if (!klass->clear_height_func)
293                 g_warning("GisViewer: clear_height_func - Unimplemented");
294         klass->clear_height_func(self);
295 }
296
297 void gis_viewer_set_height_func(GisViewer *self, GisTile *tile,
298                 GisHeightFunc height_func, gpointer user_data,
299                 gboolean update)
300 {
301         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
302         if (!klass->set_height_func)
303                 g_warning("GisViewer: set_height_func - Unimplemented");
304         klass->set_height_func(self, tile, height_func, user_data, update);
305 }
306
307 void gis_viewer_render_tile(GisViewer *self, GisTile *tile)
308 {
309         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
310         if (!klass->render_tile)
311                 g_warning("GisViewer: render_tile - Unimplemented");
312         klass->render_tile(self, tile);
313 }
314
315 void gis_viewer_render_tiles(GisViewer *self, GisTile *root)
316 {
317         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
318         if (!klass->render_tiles)
319                 g_warning("GisViewer: render_tiles - Unimplemented");
320         klass->render_tiles(self, root);
321 }
322
323 void gis_viewer_begin(GisViewer *self)
324 {
325         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
326         if (!klass->begin)
327                 g_warning("GisViewer: begin - Unimplemented");
328         klass->begin(self);
329 }
330
331 void gis_viewer_end(GisViewer *self)
332 {
333         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
334         if (!klass->end)
335                 g_warning("GisViewer: end - Unimplemented");
336         klass->end(self);
337 }
338
339 /****************
340  * GObject code *
341  ****************/
342 G_DEFINE_ABSTRACT_TYPE(GisViewer, gis_viewer, GTK_TYPE_DRAWING_AREA);
343 static void gis_viewer_init(GisViewer *self)
344 {
345         g_debug("GisViewer: init");
346         /* Default values */
347         self->time = g_strdup("");
348         self->site = g_strdup("");
349         self->location[0] = 40;
350         self->location[1] = -100;
351         self->location[2] = 1.5*EARTH_R;
352         self->rotation[0] = 0;
353         self->rotation[1] = 0;
354         self->rotation[2] = 0;
355
356         g_signal_connect(self, "key-press-event",    G_CALLBACK(on_key_press),    NULL);
357         g_signal_connect(self, "button-press-event", G_CALLBACK(on_button_press), NULL);
358
359         g_signal_connect(self, "location-changed",   G_CALLBACK(on_view_changed), NULL);
360         g_signal_connect(self, "rotation-changed",   G_CALLBACK(on_view_changed), NULL);
361 }
362 static void gis_viewer_finalize(GObject *gobject)
363 {
364         g_debug("GisViewer: finalize");
365         GisViewer *self = GIS_VIEWER(gobject);
366         g_free(self->time);
367         g_free(self->site);
368         G_OBJECT_CLASS(gis_viewer_parent_class)->finalize(gobject);
369 }
370 static void gis_viewer_set_property(GObject *object, guint property_id,
371                 const GValue *value, GParamSpec *pspec)
372 {
373         g_debug("GisViewer: set_property");
374         GisViewer *self = GIS_VIEWER(object);
375         switch (property_id) {
376         case PROP_TIME:    gis_viewer_set_time   (self, g_value_get_string (value)); break;
377         case PROP_SITE:    gis_viewer_set_site   (self, g_value_get_string (value)); break;
378         case PROP_OFFLINE: gis_viewer_set_offline(self, g_value_get_boolean(value)); break;
379         default:           G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
380         }
381 }
382 static void gis_viewer_get_property(GObject *object, guint property_id,
383                 GValue *value, GParamSpec *pspec)
384 {
385         g_debug("GisViewer: get_property");
386         GisViewer *self = GIS_VIEWER(object);
387         switch (property_id) {
388         case PROP_TIME:    g_value_set_string (value, gis_viewer_get_time   (self)); break;
389         case PROP_SITE:    g_value_set_string (value, gis_viewer_get_site   (self)); break;
390         case PROP_OFFLINE: g_value_set_boolean(value, gis_viewer_get_offline(self)); break;
391         default:           G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
392         }
393 }
394 static void gis_viewer_class_init(GisViewerClass *klass)
395 {
396         g_debug("GisViewer: class_init");
397         GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
398         gobject_class->finalize     = gis_viewer_finalize;
399         gobject_class->get_property = gis_viewer_get_property;
400         gobject_class->set_property = gis_viewer_set_property;
401         g_object_class_install_property(gobject_class, PROP_TIME,
402                 g_param_spec_pointer(
403                         "time",
404                         "time of the current frame",
405                         "(format unknown)",
406                         G_PARAM_READWRITE));
407         g_object_class_install_property(gobject_class, PROP_SITE,
408                 g_param_spec_pointer(
409                         "site",
410                         "site seen by the viewerport",
411                         "Site of the viewerport. "
412                         "Currently this is the name of the radar site.",
413                         G_PARAM_READWRITE));
414         g_object_class_install_property(gobject_class, PROP_OFFLINE,
415                 g_param_spec_pointer(
416                         "offline",
417                         "whether the viewer should access the network",
418                         "Offline state of the viewer. "
419                         "If set to true, the viewer will not access the network",
420                         G_PARAM_READWRITE));
421         signals[SIG_TIME_CHANGED] = g_signal_new(
422                         "time-changed",
423                         G_TYPE_FROM_CLASS(gobject_class),
424                         G_SIGNAL_RUN_LAST,
425                         0,
426                         NULL,
427                         NULL,
428                         g_cclosure_marshal_VOID__STRING,
429                         G_TYPE_NONE,
430                         1,
431                         G_TYPE_STRING);
432         signals[SIG_SITE_CHANGED] = g_signal_new(
433                         "site-changed",
434                         G_TYPE_FROM_CLASS(gobject_class),
435                         G_SIGNAL_RUN_LAST,
436                         0,
437                         NULL,
438                         NULL,
439                         g_cclosure_marshal_VOID__STRING,
440                         G_TYPE_NONE,
441                         1,
442                         G_TYPE_STRING);
443         signals[SIG_LOCATION_CHANGED] = g_signal_new(
444                         "location-changed",
445                         G_TYPE_FROM_CLASS(gobject_class),
446                         G_SIGNAL_RUN_LAST,
447                         0,
448                         NULL,
449                         NULL,
450                         gis_cclosure_marshal_VOID__DOUBLE_DOUBLE_DOUBLE,
451                         G_TYPE_NONE,
452                         3,
453                         G_TYPE_DOUBLE,
454                         G_TYPE_DOUBLE,
455                         G_TYPE_DOUBLE);
456         signals[SIG_ROTATION_CHANGED] = g_signal_new(
457                         "rotation-changed",
458                         G_TYPE_FROM_CLASS(gobject_class),
459                         G_SIGNAL_RUN_LAST,
460                         0,
461                         NULL,
462                         NULL,
463                         gis_cclosure_marshal_VOID__DOUBLE_DOUBLE_DOUBLE,
464                         G_TYPE_NONE,
465                         3,
466                         G_TYPE_DOUBLE,
467                         G_TYPE_DOUBLE,
468                         G_TYPE_DOUBLE);
469         signals[SIG_REFRESH] = g_signal_new(
470                         "refresh",
471                         G_TYPE_FROM_CLASS(gobject_class),
472                         G_SIGNAL_RUN_LAST,
473                         0,
474                         NULL,
475                         NULL,
476                         g_cclosure_marshal_VOID__VOID,
477                         G_TYPE_NONE,
478                         0);
479         signals[SIG_OFFLINE] = g_signal_new(
480                         "offline",
481                         G_TYPE_FROM_CLASS(gobject_class),
482                         G_SIGNAL_RUN_LAST,
483                         0,
484                         NULL,
485                         NULL,
486                         g_cclosure_marshal_VOID__BOOLEAN,
487                         G_TYPE_NONE,
488                         1,
489                         G_TYPE_BOOLEAN);
490 }