]> Pileus Git - grits/blob - src/gis-viewer.c
947643d70dc96b200fa566f38fc9d593204c0649
[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         SIG_TIME_CHANGED,
31         SIG_LOCATION_CHANGED,
32         SIG_ROTATION_CHANGED,
33         SIG_REFRESH,
34         SIG_OFFLINE,
35         NUM_SIGNALS,
36 };
37 static guint signals[NUM_SIGNALS];
38
39
40 /***********
41  * Helpers *
42  ***********/
43 /* Misc helpers */
44 static void _gis_viewer_fix_location(GisViewer *self)
45 {
46         while (self->location[0] <  -90) self->location[0] += 180;
47         while (self->location[0] >   90) self->location[0] -= 180;
48         while (self->location[1] < -180) self->location[1] += 360;
49         while (self->location[1] >  180) self->location[1] -= 360;
50         self->location[2] = ABS(self->location[2]);
51 }
52
53 /* Signal helpers */
54 static void _gis_viewer_emit_location_changed(GisViewer *self)
55 {
56         g_signal_emit(self, signals[SIG_LOCATION_CHANGED], 0,
57                         self->location[0],
58                         self->location[1],
59                         self->location[2]);
60 }
61 static void _gis_viewer_emit_rotation_changed(GisViewer *self)
62 {
63         g_signal_emit(self, signals[SIG_ROTATION_CHANGED], 0,
64                         self->rotation[0],
65                         self->rotation[1],
66                         self->rotation[2]);
67 }
68 static void _gis_viewer_emit_time_changed(GisViewer *self)
69 {
70         g_signal_emit(self, signals[SIG_TIME_CHANGED], 0,
71                         self->time);
72 }
73 static void _gis_viewer_emit_refresh(GisViewer *self)
74 {
75         g_signal_emit(self, signals[SIG_REFRESH], 0);
76 }
77 static void _gis_viewer_emit_offline(GisViewer *self)
78 {
79         g_signal_emit(self, signals[SIG_OFFLINE], 0,
80                         self->offline);
81 }
82
83 /*************
84  * Callbacks *
85  *************/
86 static gboolean on_button_press(GisViewer *self, GdkEventButton *event, gpointer _)
87 {
88         g_debug("GisViewer: on_button_press - Grabbing focus");
89         gtk_widget_grab_focus(GTK_WIDGET(self));
90         return TRUE;
91 }
92 static gboolean on_key_press(GisViewer *self, GdkEventKey *event, gpointer _)
93 {
94         g_debug("GisViewer: on_key_press - key=%x, state=%x, plus=%x",
95                         event->keyval, event->state, GDK_plus);
96
97         double lat, lon, elev, pan;
98         gis_viewer_get_location(self, &lat, &lon, &elev);
99         pan = MIN(elev/(EARTH_R/2), 30);
100         guint kv = event->keyval;
101         gdk_threads_leave();
102         if      (kv == GDK_Left  || kv == GDK_h) gis_viewer_pan(self,  0,  -pan, 0);
103         else if (kv == GDK_Down  || kv == GDK_j) gis_viewer_pan(self, -pan, 0,   0);
104         else if (kv == GDK_Up    || kv == GDK_k) gis_viewer_pan(self,  pan, 0,   0);
105         else if (kv == GDK_Right || kv == GDK_l) gis_viewer_pan(self,  0,   pan, 0);
106         else if (kv == GDK_minus || kv == GDK_o) gis_viewer_zoom(self, 10./9);
107         else if (kv == GDK_plus  || kv == GDK_i) gis_viewer_zoom(self, 9./10);
108         else if (kv == GDK_H) gis_viewer_rotate(self,  0, 0, -2);
109         else if (kv == GDK_J) gis_viewer_rotate(self,  2, 0,  0);
110         else if (kv == GDK_K) gis_viewer_rotate(self, -2, 0,  0);
111         else if (kv == GDK_L) gis_viewer_rotate(self,  0, 0,  2);
112         return FALSE;
113 }
114 static void on_view_changed(GisViewer *self,
115                 gdouble _1, gdouble _2, gdouble _3)
116 {
117         gtk_widget_queue_draw(GTK_WIDGET(self));
118 }
119
120 /***********
121  * Methods *
122  ***********/
123 void gis_viewer_set_time(GisViewer *self, const char *time)
124 {
125         g_assert(GIS_IS_VIEWER(self));
126         g_debug("GisViewer: set_time - time=%s", time);
127         g_free(self->time);
128         self->time = g_strdup(time);
129         _gis_viewer_emit_time_changed(self);
130 }
131
132 gchar *gis_viewer_get_time(GisViewer *self)
133 {
134         g_assert(GIS_IS_VIEWER(self));
135         g_debug("GisViewer: get_time");
136         return self->time;
137 }
138
139 void gis_viewer_set_location(GisViewer *self, gdouble lat, gdouble lon, gdouble elev)
140 {
141         g_assert(GIS_IS_VIEWER(self));
142         g_debug("GisViewer: set_location");
143         self->location[0] = lat;
144         self->location[1] = lon;
145         self->location[2] = elev;
146         _gis_viewer_fix_location(self);
147         _gis_viewer_emit_location_changed(self);
148 }
149
150 void gis_viewer_get_location(GisViewer *self, gdouble *lat, gdouble *lon, gdouble *elev)
151 {
152         g_assert(GIS_IS_VIEWER(self));
153         //g_debug("GisViewer: get_location");
154         *lat  = self->location[0];
155         *lon  = self->location[1];
156         *elev = self->location[2];
157 }
158
159 void gis_viewer_pan(GisViewer *self, gdouble lat, gdouble lon, gdouble elev)
160 {
161         g_assert(GIS_IS_VIEWER(self));
162         g_debug("GisViewer: pan - lat=%8.3f, lon=%8.3f, elev=%8.3f", lat, lon, elev);
163         self->location[0] += lat;
164         self->location[1] += lon;
165         self->location[2] += elev;
166         _gis_viewer_fix_location(self);
167         _gis_viewer_emit_location_changed(self);
168 }
169
170 void gis_viewer_zoom(GisViewer *self, gdouble scale)
171 {
172         g_assert(GIS_IS_VIEWER(self));
173         g_debug("GisViewer: zoom");
174         self->location[2] *= scale;
175         _gis_viewer_emit_location_changed(self);
176 }
177
178 void gis_viewer_set_rotation(GisViewer *self, gdouble x, gdouble y, gdouble z)
179 {
180         g_assert(GIS_IS_VIEWER(self));
181         g_debug("GisViewer: set_rotation");
182         self->rotation[0] = x;
183         self->rotation[1] = y;
184         self->rotation[2] = z;
185         _gis_viewer_emit_rotation_changed(self);
186 }
187
188 void gis_viewer_get_rotation(GisViewer *self, gdouble *x, gdouble *y, gdouble *z)
189 {
190         g_assert(GIS_IS_VIEWER(self));
191         g_debug("GisViewer: get_rotation");
192         *x = self->rotation[0];
193         *y = self->rotation[1];
194         *z = self->rotation[2];
195 }
196
197 void gis_viewer_rotate(GisViewer *self, gdouble x, gdouble y, gdouble z)
198 {
199         g_assert(GIS_IS_VIEWER(self));
200         g_debug("GisViewer: rotate - x=%.0f, y=%.0f, z=%.0f", x, y, z);
201         self->rotation[0] += x;
202         self->rotation[1] += y;
203         self->rotation[2] += z;
204         _gis_viewer_emit_rotation_changed(self);
205 }
206
207 void gis_viewer_refresh(GisViewer *self)
208 {
209         g_debug("GisViewer: refresh");
210         _gis_viewer_emit_refresh(self);
211 }
212
213 void gis_viewer_set_offline(GisViewer *self, gboolean offline)
214 {
215         g_assert(GIS_IS_VIEWER(self));
216         g_debug("GisViewer: set_offline - %d", offline);
217         self->offline = offline;
218         _gis_viewer_emit_offline(self);
219 }
220
221 gboolean gis_viewer_get_offline(GisViewer *self)
222 {
223         g_assert(GIS_IS_VIEWER(self));
224         g_debug("GisViewer: get_offline - %d", self->offline);
225         return self->offline;
226 }
227
228 /* To be implemented by subclasses */
229 void gis_viewer_center_position(GisViewer *self,
230                 gdouble lat, gdouble lon, gdouble elev)
231 {
232         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
233         if (!klass->center_position)
234                 g_warning("GisViewer: center_position - Unimplemented");
235         klass->center_position(self, lat, lon, elev);
236 }
237
238 void gis_viewer_project(GisViewer *self,
239                 gdouble lat, gdouble lon, gdouble elev,
240                 gdouble *px, gdouble *py, gdouble *pz)
241 {
242         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
243         if (!klass->project)
244                 g_warning("GisViewer: project - Unimplemented");
245         klass->project(self, lat, lon, elev, px, py, pz);
246 }
247
248 void gis_viewer_clear_height_func(GisViewer *self)
249 {
250         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
251         if (!klass->clear_height_func)
252                 g_warning("GisViewer: clear_height_func - Unimplemented");
253         klass->clear_height_func(self);
254 }
255
256 void gis_viewer_set_height_func(GisViewer *self, GisTile *tile,
257                 GisHeightFunc height_func, gpointer user_data,
258                 gboolean update)
259 {
260         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
261         if (!klass->set_height_func)
262                 g_warning("GisViewer: set_height_func - Unimplemented");
263         klass->set_height_func(self, tile, height_func, user_data, update);
264 }
265
266 void gis_viewer_render_tile(GisViewer *self, GisTile *tile)
267 {
268         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
269         if (!klass->render_tile)
270                 g_warning("GisViewer: render_tile - Unimplemented");
271         klass->render_tile(self, tile);
272 }
273
274 void gis_viewer_render_tiles(GisViewer *self, GisTile *root)
275 {
276         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
277         if (!klass->render_tiles)
278                 g_warning("GisViewer: render_tiles - Unimplemented");
279         klass->render_tiles(self, root);
280 }
281
282 void gis_viewer_begin(GisViewer *self)
283 {
284         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
285         if (!klass->begin)
286                 g_warning("GisViewer: begin - Unimplemented");
287         klass->begin(self);
288 }
289
290 void gis_viewer_end(GisViewer *self)
291 {
292         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
293         if (!klass->end)
294                 g_warning("GisViewer: end - Unimplemented");
295         klass->end(self);
296 }
297
298 gpointer gis_viewer_add(GisViewer *self, GisObject *object,
299                 gint level, gboolean sort)
300 {
301         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
302         if (!klass->add)
303                 g_warning("GisViewer: add - Unimplemented");
304         return klass->add(self, object, level, sort);
305 }
306
307 void gis_viewer_remove(GisViewer *self, gpointer ref)
308 {
309         GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
310         if (!klass->remove)
311                 g_warning("GisViewer: remove - Unimplemented");
312         klass->remove(self, ref);
313 }
314
315 /****************
316  * GObject code *
317  ****************/
318 G_DEFINE_ABSTRACT_TYPE(GisViewer, gis_viewer, GTK_TYPE_DRAWING_AREA);
319 static void gis_viewer_init(GisViewer *self)
320 {
321         g_debug("GisViewer: init");
322         /* Default values */
323         self->time = g_strdup("");
324         self->location[0] = 40;
325         self->location[1] = -100;
326         self->location[2] = 1.5*EARTH_R;
327         self->rotation[0] = 0;
328         self->rotation[1] = 0;
329         self->rotation[2] = 0;
330
331         g_signal_connect(self, "key-press-event",    G_CALLBACK(on_key_press),    NULL);
332         g_signal_connect(self, "button-press-event", G_CALLBACK(on_button_press), NULL);
333
334         g_signal_connect(self, "location-changed",   G_CALLBACK(on_view_changed), NULL);
335         g_signal_connect(self, "rotation-changed",   G_CALLBACK(on_view_changed), NULL);
336 }
337 static void gis_viewer_finalize(GObject *gobject)
338 {
339         g_debug("GisViewer: finalize");
340         GisViewer *self = GIS_VIEWER(gobject);
341         g_free(self->time);
342         G_OBJECT_CLASS(gis_viewer_parent_class)->finalize(gobject);
343 }
344 static void gis_viewer_class_init(GisViewerClass *klass)
345 {
346         g_debug("GisViewer: class_init");
347         GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
348         gobject_class->finalize     = gis_viewer_finalize;
349         signals[SIG_TIME_CHANGED] = g_signal_new(
350                         "time-changed",
351                         G_TYPE_FROM_CLASS(gobject_class),
352                         G_SIGNAL_RUN_LAST,
353                         0,
354                         NULL,
355                         NULL,
356                         g_cclosure_marshal_VOID__STRING,
357                         G_TYPE_NONE,
358                         1,
359                         G_TYPE_STRING);
360         signals[SIG_LOCATION_CHANGED] = g_signal_new(
361                         "location-changed",
362                         G_TYPE_FROM_CLASS(gobject_class),
363                         G_SIGNAL_RUN_LAST,
364                         0,
365                         NULL,
366                         NULL,
367                         gis_cclosure_marshal_VOID__DOUBLE_DOUBLE_DOUBLE,
368                         G_TYPE_NONE,
369                         3,
370                         G_TYPE_DOUBLE,
371                         G_TYPE_DOUBLE,
372                         G_TYPE_DOUBLE);
373         signals[SIG_ROTATION_CHANGED] = g_signal_new(
374                         "rotation-changed",
375                         G_TYPE_FROM_CLASS(gobject_class),
376                         G_SIGNAL_RUN_LAST,
377                         0,
378                         NULL,
379                         NULL,
380                         gis_cclosure_marshal_VOID__DOUBLE_DOUBLE_DOUBLE,
381                         G_TYPE_NONE,
382                         3,
383                         G_TYPE_DOUBLE,
384                         G_TYPE_DOUBLE,
385                         G_TYPE_DOUBLE);
386         signals[SIG_REFRESH] = g_signal_new(
387                         "refresh",
388                         G_TYPE_FROM_CLASS(gobject_class),
389                         G_SIGNAL_RUN_LAST,
390                         0,
391                         NULL,
392                         NULL,
393                         g_cclosure_marshal_VOID__VOID,
394                         G_TYPE_NONE,
395                         0);
396         signals[SIG_OFFLINE] = g_signal_new(
397                         "offline",
398                         G_TYPE_FROM_CLASS(gobject_class),
399                         G_SIGNAL_RUN_LAST,
400                         0,
401                         NULL,
402                         NULL,
403                         g_cclosure_marshal_VOID__BOOLEAN,
404                         G_TYPE_NONE,
405                         1,
406                         G_TYPE_BOOLEAN);
407 }