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 void gis_viewer_add_object(GisViewer *self, GisObject *object)
259 g_debug("GisViewer: add_object - %d, %p", object->type, object);
260 self->objects = g_list_prepend(self->objects, object);
263 void gis_viewer_remove_object(GisViewer *self, GisObject *object)
265 g_debug("GisViewer: remove_object - %d, %p", object->type, object);
266 self->objects = g_list_remove(self->objects, object);
269 /* To be implemented by subclasses */
270 void gis_viewer_center_position(GisViewer *self,
271 gdouble lat, gdouble lon, gdouble elev)
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);
279 void gis_viewer_project(GisViewer *self,
280 gdouble lat, gdouble lon, gdouble elev,
281 gdouble *px, gdouble *py, gdouble *pz)
283 GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
285 g_warning("GisViewer: project - Unimplemented");
286 klass->project(self, lat, lon, elev, px, py, pz);
289 void gis_viewer_clear_height_func(GisViewer *self)
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);
297 void gis_viewer_set_height_func(GisViewer *self, GisTile *tile,
298 GisHeightFunc height_func, gpointer user_data,
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);
307 void gis_viewer_render_tile(GisViewer *self, GisTile *tile)
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);
315 void gis_viewer_render_tiles(GisViewer *self, GisTile *root)
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);
323 void gis_viewer_begin(GisViewer *self)
325 GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
327 g_warning("GisViewer: begin - Unimplemented");
331 void gis_viewer_end(GisViewer *self)
333 GisViewerClass *klass = GIS_VIEWER_GET_CLASS(self);
335 g_warning("GisViewer: end - Unimplemented");
342 G_DEFINE_ABSTRACT_TYPE(GisViewer, gis_viewer, GTK_TYPE_DRAWING_AREA);
343 static void gis_viewer_init(GisViewer *self)
345 g_debug("GisViewer: init");
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;
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);
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);
362 static void gis_viewer_finalize(GObject *gobject)
364 g_debug("GisViewer: finalize");
365 GisViewer *self = GIS_VIEWER(gobject);
368 G_OBJECT_CLASS(gis_viewer_parent_class)->finalize(gobject);
370 static void gis_viewer_set_property(GObject *object, guint property_id,
371 const GValue *value, GParamSpec *pspec)
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);
382 static void gis_viewer_get_property(GObject *object, guint property_id,
383 GValue *value, GParamSpec *pspec)
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);
394 static void gis_viewer_class_init(GisViewerClass *klass)
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(
404 "time of the current frame",
407 g_object_class_install_property(gobject_class, PROP_SITE,
408 g_param_spec_pointer(
410 "site seen by the viewerport",
411 "Site of the viewerport. "
412 "Currently this is the name of the radar site.",
414 g_object_class_install_property(gobject_class, PROP_OFFLINE,
415 g_param_spec_pointer(
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",
421 signals[SIG_TIME_CHANGED] = g_signal_new(
423 G_TYPE_FROM_CLASS(gobject_class),
428 g_cclosure_marshal_VOID__STRING,
432 signals[SIG_SITE_CHANGED] = g_signal_new(
434 G_TYPE_FROM_CLASS(gobject_class),
439 g_cclosure_marshal_VOID__STRING,
443 signals[SIG_LOCATION_CHANGED] = g_signal_new(
445 G_TYPE_FROM_CLASS(gobject_class),
450 gis_cclosure_marshal_VOID__DOUBLE_DOUBLE_DOUBLE,
456 signals[SIG_ROTATION_CHANGED] = g_signal_new(
458 G_TYPE_FROM_CLASS(gobject_class),
463 gis_cclosure_marshal_VOID__DOUBLE_DOUBLE_DOUBLE,
469 signals[SIG_REFRESH] = g_signal_new(
471 G_TYPE_FROM_CLASS(gobject_class),
476 g_cclosure_marshal_VOID__VOID,
479 signals[SIG_OFFLINE] = g_signal_new(
481 G_TYPE_FROM_CLASS(gobject_class),
486 g_cclosure_marshal_VOID__BOOLEAN,