2 * Copyright (C) 2009 Andy Spencer <spenceal@rose-hulman.edu>
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.
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.
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/>.
20 #include <gdk/gdkkeysyms.h>
22 #include "gis-marshal.h"
23 #include "gis-viewer.h"
44 static guint signals[NUM_SIGNALS];
51 static void _gis_viewer_fix_location(GisViewer *self)
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]);
61 static void _gis_viewer_emit_location_changed(GisViewer *self)
63 g_signal_emit(self, signals[SIG_LOCATION_CHANGED], 0,
68 static void _gis_viewer_emit_rotation_changed(GisViewer *self)
70 g_signal_emit(self, signals[SIG_ROTATION_CHANGED], 0,
75 static void _gis_viewer_emit_time_changed(GisViewer *self)
77 g_signal_emit(self, signals[SIG_TIME_CHANGED], 0,
80 static void _gis_viewer_emit_site_changed(GisViewer *self)
82 g_signal_emit(self, signals[SIG_SITE_CHANGED], 0,
85 static void _gis_viewer_emit_refresh(GisViewer *self)
87 g_signal_emit(self, signals[SIG_REFRESH], 0);
89 static void _gis_viewer_emit_offline(GisViewer *self)
91 g_signal_emit(self, signals[SIG_OFFLINE], 0,
98 static gboolean on_button_press(GisViewer *self, GdkEventButton *event, gpointer _)
100 g_debug("GisViewer: on_button_press - Grabbing focus");
101 gtk_widget_grab_focus(GTK_WIDGET(self));
104 static gboolean on_key_press(GisViewer *self, GdkEventKey *event, gpointer _)
106 g_debug("GisViewer: on_key_press - key=%x, state=%x, plus=%x",
107 event->keyval, event->state, GDK_plus);
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;
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);
126 static void on_view_changed(GisViewer *self,
127 gdouble _1, gdouble _2, gdouble _3)
129 gtk_widget_queue_draw(GTK_WIDGET(self));
135 void gis_viewer_set_time(GisViewer *self, const char *time)
137 g_assert(GIS_IS_VIEWER(self));
138 g_debug("GisViewer: set_time - time=%s", time);
140 self->time = g_strdup(time);
141 _gis_viewer_emit_time_changed(self);
144 gchar *gis_viewer_get_time(GisViewer *self)
146 g_assert(GIS_IS_VIEWER(self));
147 g_debug("GisViewer: get_time");
151 void gis_viewer_set_location(GisViewer *self, gdouble lat, gdouble lon, gdouble elev)
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);
162 void gis_viewer_get_location(GisViewer *self, gdouble *lat, gdouble *lon, gdouble *elev)
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];
171 void gis_viewer_pan(GisViewer *self, gdouble lat, gdouble lon, gdouble elev)
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);
182 void gis_viewer_zoom(GisViewer *self, gdouble scale)
184 g_assert(GIS_IS_VIEWER(self));
185 g_debug("GisViewer: zoom");
186 self->location[2] *= scale;
187 _gis_viewer_emit_location_changed(self);
190 void gis_viewer_set_rotation(GisViewer *self, gdouble x, gdouble y, gdouble z)
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);
200 void gis_viewer_get_rotation(GisViewer *self, gdouble *x, gdouble *y, gdouble *z)
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];
209 void gis_viewer_rotate(GisViewer *self, gdouble x, gdouble y, gdouble z)
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);
219 /* To be deprecated, use {get,set}_location */
220 void gis_viewer_set_site(GisViewer *self, const gchar *site)
222 g_assert(GIS_IS_VIEWER(self));
223 g_debug("GisViewer: set_site");
225 self->site = g_strdup(site);
226 _gis_viewer_emit_site_changed(self);
229 gchar *gis_viewer_get_site(GisViewer *self)
231 g_assert(GIS_IS_VIEWER(self));
232 g_debug("GisViewer: get_site - %s", self->site);
236 void gis_viewer_refresh(GisViewer *self)
238 g_debug("GisViewer: refresh");
239 _gis_viewer_emit_refresh(self);
242 void gis_viewer_set_offline(GisViewer *self, gboolean offline)
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);
250 gboolean gis_viewer_get_offline(GisViewer *self)
252 g_assert(GIS_IS_VIEWER(self));
253 g_debug("GisViewer: get_offline - %d", self->offline);
254 return self->offline;
257 /* To be implemented by subclasses */
258 void gis_viewer_center_position(GisViewer *self,
259 gdouble lat, gdouble lon, gdouble elev)
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);
267 void gis_viewer_project(GisViewer *self,
268 gdouble lat, gdouble lon, gdouble elev,
269 gdouble *px, gdouble *py, gdouble *pz)
271 GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
273 g_warning("GisViewer: project - Unimplemented");
274 klass->project(self, lat, lon, elev, px, py, pz);
277 void gis_viewer_clear_height_func(GisViewer *self)
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);
285 void gis_viewer_set_height_func(GisViewer *self, GisTile *tile,
286 GisHeightFunc height_func, gpointer user_data,
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);
295 void gis_viewer_render_tile(GisViewer *self, GisTile *tile)
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);
303 void gis_viewer_render_tiles(GisViewer *self, GisTile *root)
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);
311 void gis_viewer_begin(GisViewer *self)
313 GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
315 g_warning("GisViewer: begin - Unimplemented");
319 void gis_viewer_end(GisViewer *self)
321 GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
323 g_warning("GisViewer: end - Unimplemented");
327 void gis_viewer_add(GisViewer *self, GisObject *object)
329 GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
331 g_warning("GisViewer: add - Unimplemented");
332 klass->add(self, object);
335 void gis_viewer_remove(GisViewer *self, GisObject *object)
337 GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
339 g_warning("GisViewer: remove - Unimplemented");
340 klass->remove(self, object);
346 G_DEFINE_ABSTRACT_TYPE(GisViewer, gis_viewer, GTK_TYPE_DRAWING_AREA);
347 static void gis_viewer_init(GisViewer *self)
349 g_debug("GisViewer: init");
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;
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);
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);
366 static void gis_viewer_finalize(GObject *gobject)
368 g_debug("GisViewer: finalize");
369 GisViewer *self = GIS_VIEWER(gobject);
372 G_OBJECT_CLASS(gis_viewer_parent_class)->finalize(gobject);
374 static void gis_viewer_set_property(GObject *object, guint property_id,
375 const GValue *value, GParamSpec *pspec)
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);
386 static void gis_viewer_get_property(GObject *object, guint property_id,
387 GValue *value, GParamSpec *pspec)
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);
398 static void gis_viewer_class_init(GisViewerClass *klass)
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(
408 "time of the current frame",
411 g_object_class_install_property(gobject_class, PROP_SITE,
412 g_param_spec_pointer(
414 "site seen by the viewerport",
415 "Site of the viewerport. "
416 "Currently this is the name of the radar site.",
418 g_object_class_install_property(gobject_class, PROP_OFFLINE,
419 g_param_spec_pointer(
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",
425 signals[SIG_TIME_CHANGED] = g_signal_new(
427 G_TYPE_FROM_CLASS(gobject_class),
432 g_cclosure_marshal_VOID__STRING,
436 signals[SIG_SITE_CHANGED] = g_signal_new(
438 G_TYPE_FROM_CLASS(gobject_class),
443 g_cclosure_marshal_VOID__STRING,
447 signals[SIG_LOCATION_CHANGED] = g_signal_new(
449 G_TYPE_FROM_CLASS(gobject_class),
454 gis_cclosure_marshal_VOID__DOUBLE_DOUBLE_DOUBLE,
460 signals[SIG_ROTATION_CHANGED] = g_signal_new(
462 G_TYPE_FROM_CLASS(gobject_class),
467 gis_cclosure_marshal_VOID__DOUBLE_DOUBLE_DOUBLE,
473 signals[SIG_REFRESH] = g_signal_new(
475 G_TYPE_FROM_CLASS(gobject_class),
480 g_cclosure_marshal_VOID__VOID,
483 signals[SIG_OFFLINE] = g_signal_new(
485 G_TYPE_FROM_CLASS(gobject_class),
490 g_cclosure_marshal_VOID__BOOLEAN,