]> Pileus Git - grits/blob - src/gis-viewer.c
Markers as individual textures
[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 /* To be implemented by subclasses */
258 void gis_viewer_center_position(GisViewer *self,
259                 gdouble lat, gdouble lon, gdouble elev)
260 {
261         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
262         if (!klass->center_position)
263                 g_warning("GisViewer: center_position - Unimplemented");
264         klass->center_position(self, lat, lon, elev);
265 }
266
267 void gis_viewer_project(GisViewer *self,
268                 gdouble lat, gdouble lon, gdouble elev,
269                 gdouble *px, gdouble *py, gdouble *pz)
270 {
271         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
272         if (!klass->project)
273                 g_warning("GisViewer: project - Unimplemented");
274         klass->project(self, lat, lon, elev, px, py, pz);
275 }
276
277 void gis_viewer_clear_height_func(GisViewer *self)
278 {
279         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
280         if (!klass->clear_height_func)
281                 g_warning("GisViewer: clear_height_func - Unimplemented");
282         klass->clear_height_func(self);
283 }
284
285 void gis_viewer_set_height_func(GisViewer *self, GisTile *tile,
286                 GisHeightFunc height_func, gpointer user_data,
287                 gboolean update)
288 {
289         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
290         if (!klass->set_height_func)
291                 g_warning("GisViewer: set_height_func - Unimplemented");
292         klass->set_height_func(self, tile, height_func, user_data, update);
293 }
294
295 void gis_viewer_render_tile(GisViewer *self, GisTile *tile)
296 {
297         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
298         if (!klass->render_tile)
299                 g_warning("GisViewer: render_tile - Unimplemented");
300         klass->render_tile(self, tile);
301 }
302
303 void gis_viewer_render_tiles(GisViewer *self, GisTile *root)
304 {
305         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
306         if (!klass->render_tiles)
307                 g_warning("GisViewer: render_tiles - Unimplemented");
308         klass->render_tiles(self, root);
309 }
310
311 void gis_viewer_begin(GisViewer *self)
312 {
313         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
314         if (!klass->begin)
315                 g_warning("GisViewer: begin - Unimplemented");
316         klass->begin(self);
317 }
318
319 void gis_viewer_end(GisViewer *self)
320 {
321         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
322         if (!klass->end)
323                 g_warning("GisViewer: end - Unimplemented");
324         klass->end(self);
325 }
326
327 void gis_viewer_add(GisViewer *self, GisObject *object)
328 {
329         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
330         if (!klass->add)
331                 g_warning("GisViewer: add - Unimplemented");
332         klass->add(self, object);
333 }
334
335 void gis_viewer_remove(GisViewer *self, GisObject *object)
336 {
337         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
338         if (!klass->remove)
339                 g_warning("GisViewer: remove - Unimplemented");
340         klass->remove(self, object);
341 }
342
343 /****************
344  * GObject code *
345  ****************/
346 G_DEFINE_ABSTRACT_TYPE(GisViewer, gis_viewer, GTK_TYPE_DRAWING_AREA);
347 static void gis_viewer_init(GisViewer *self)
348 {
349         g_debug("GisViewer: init");
350         /* Default values */
351         self->time = g_strdup("");
352         self->site = g_strdup("");
353         self->location[0] = 40;
354         self->location[1] = -100;
355         self->location[2] = 1.5*EARTH_R;
356         self->rotation[0] = 0;
357         self->rotation[1] = 0;
358         self->rotation[2] = 0;
359
360         g_signal_connect(self, "key-press-event",    G_CALLBACK(on_key_press),    NULL);
361         g_signal_connect(self, "button-press-event", G_CALLBACK(on_button_press), NULL);
362
363         g_signal_connect(self, "location-changed",   G_CALLBACK(on_view_changed), NULL);
364         g_signal_connect(self, "rotation-changed",   G_CALLBACK(on_view_changed), NULL);
365 }
366 static void gis_viewer_finalize(GObject *gobject)
367 {
368         g_debug("GisViewer: finalize");
369         GisViewer *self = GIS_VIEWER(gobject);
370         g_free(self->time);
371         g_free(self->site);
372         G_OBJECT_CLASS(gis_viewer_parent_class)->finalize(gobject);
373 }
374 static void gis_viewer_set_property(GObject *object, guint property_id,
375                 const GValue *value, GParamSpec *pspec)
376 {
377         g_debug("GisViewer: set_property");
378         GisViewer *self = GIS_VIEWER(object);
379         switch (property_id) {
380         case PROP_TIME:    gis_viewer_set_time   (self, g_value_get_string (value)); break;
381         case PROP_SITE:    gis_viewer_set_site   (self, g_value_get_string (value)); break;
382         case PROP_OFFLINE: gis_viewer_set_offline(self, g_value_get_boolean(value)); break;
383         default:           G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
384         }
385 }
386 static void gis_viewer_get_property(GObject *object, guint property_id,
387                 GValue *value, GParamSpec *pspec)
388 {
389         g_debug("GisViewer: get_property");
390         GisViewer *self = GIS_VIEWER(object);
391         switch (property_id) {
392         case PROP_TIME:    g_value_set_string (value, gis_viewer_get_time   (self)); break;
393         case PROP_SITE:    g_value_set_string (value, gis_viewer_get_site   (self)); break;
394         case PROP_OFFLINE: g_value_set_boolean(value, gis_viewer_get_offline(self)); break;
395         default:           G_OBJECT_WARN_INVALID_PROPERTY_ID(object, property_id, pspec);
396         }
397 }
398 static void gis_viewer_class_init(GisViewerClass *klass)
399 {
400         g_debug("GisViewer: class_init");
401         GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
402         gobject_class->finalize     = gis_viewer_finalize;
403         gobject_class->get_property = gis_viewer_get_property;
404         gobject_class->set_property = gis_viewer_set_property;
405         g_object_class_install_property(gobject_class, PROP_TIME,
406                 g_param_spec_pointer(
407                         "time",
408                         "time of the current frame",
409                         "(format unknown)",
410                         G_PARAM_READWRITE));
411         g_object_class_install_property(gobject_class, PROP_SITE,
412                 g_param_spec_pointer(
413                         "site",
414                         "site seen by the viewerport",
415                         "Site of the viewerport. "
416                         "Currently this is the name of the radar site.",
417                         G_PARAM_READWRITE));
418         g_object_class_install_property(gobject_class, PROP_OFFLINE,
419                 g_param_spec_pointer(
420                         "offline",
421                         "whether the viewer should access the network",
422                         "Offline state of the viewer. "
423                         "If set to true, the viewer will not access the network",
424                         G_PARAM_READWRITE));
425         signals[SIG_TIME_CHANGED] = g_signal_new(
426                         "time-changed",
427                         G_TYPE_FROM_CLASS(gobject_class),
428                         G_SIGNAL_RUN_LAST,
429                         0,
430                         NULL,
431                         NULL,
432                         g_cclosure_marshal_VOID__STRING,
433                         G_TYPE_NONE,
434                         1,
435                         G_TYPE_STRING);
436         signals[SIG_SITE_CHANGED] = g_signal_new(
437                         "site-changed",
438                         G_TYPE_FROM_CLASS(gobject_class),
439                         G_SIGNAL_RUN_LAST,
440                         0,
441                         NULL,
442                         NULL,
443                         g_cclosure_marshal_VOID__STRING,
444                         G_TYPE_NONE,
445                         1,
446                         G_TYPE_STRING);
447         signals[SIG_LOCATION_CHANGED] = g_signal_new(
448                         "location-changed",
449                         G_TYPE_FROM_CLASS(gobject_class),
450                         G_SIGNAL_RUN_LAST,
451                         0,
452                         NULL,
453                         NULL,
454                         gis_cclosure_marshal_VOID__DOUBLE_DOUBLE_DOUBLE,
455                         G_TYPE_NONE,
456                         3,
457                         G_TYPE_DOUBLE,
458                         G_TYPE_DOUBLE,
459                         G_TYPE_DOUBLE);
460         signals[SIG_ROTATION_CHANGED] = g_signal_new(
461                         "rotation-changed",
462                         G_TYPE_FROM_CLASS(gobject_class),
463                         G_SIGNAL_RUN_LAST,
464                         0,
465                         NULL,
466                         NULL,
467                         gis_cclosure_marshal_VOID__DOUBLE_DOUBLE_DOUBLE,
468                         G_TYPE_NONE,
469                         3,
470                         G_TYPE_DOUBLE,
471                         G_TYPE_DOUBLE,
472                         G_TYPE_DOUBLE);
473         signals[SIG_REFRESH] = g_signal_new(
474                         "refresh",
475                         G_TYPE_FROM_CLASS(gobject_class),
476                         G_SIGNAL_RUN_LAST,
477                         0,
478                         NULL,
479                         NULL,
480                         g_cclosure_marshal_VOID__VOID,
481                         G_TYPE_NONE,
482                         0);
483         signals[SIG_OFFLINE] = g_signal_new(
484                         "offline",
485                         G_TYPE_FROM_CLASS(gobject_class),
486                         G_SIGNAL_RUN_LAST,
487                         0,
488                         NULL,
489                         NULL,
490                         g_cclosure_marshal_VOID__BOOLEAN,
491                         G_TYPE_NONE,
492                         1,
493                         G_TYPE_BOOLEAN);
494 }