2 * Copyright (C) 2009-2010 Andy Spencer <andy753421@gmail.com>
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/>.
21 #include <gdk/gdkkeysyms.h>
23 #include "gis-marshal.h"
24 #include "gis-viewer.h"
38 static guint signals[NUM_SIGNALS];
45 static void _gis_viewer_fix_location(GisViewer *viewer)
47 while (viewer->location[0] < -90) viewer->location[0] += 180;
48 while (viewer->location[0] > 90) viewer->location[0] -= 180;
49 while (viewer->location[1] < -180) viewer->location[1] += 360;
50 while (viewer->location[1] > 180) viewer->location[1] -= 360;
51 viewer->location[2] = ABS(viewer->location[2]);
55 static void _gis_viewer_emit_location_changed(GisViewer *viewer)
57 g_signal_emit(viewer, signals[SIG_LOCATION_CHANGED], 0,
62 static void _gis_viewer_emit_rotation_changed(GisViewer *viewer)
64 g_signal_emit(viewer, signals[SIG_ROTATION_CHANGED], 0,
69 static void _gis_viewer_emit_time_changed(GisViewer *viewer)
71 g_signal_emit(viewer, signals[SIG_TIME_CHANGED], 0,
74 static void _gis_viewer_emit_refresh(GisViewer *viewer)
76 g_signal_emit(viewer, signals[SIG_REFRESH], 0);
78 static void _gis_viewer_emit_offline(GisViewer *viewer)
80 g_signal_emit(viewer, signals[SIG_OFFLINE], 0,
87 static gboolean on_key_press(GisViewer *viewer, GdkEventKey *event, gpointer _)
89 g_debug("GisViewer: on_key_press - key=%x, state=%x, plus=%x",
90 event->keyval, event->state, GDK_plus);
92 double lat, lon, elev, pan;
93 gis_viewer_get_location(viewer, &lat, &lon, &elev);
94 pan = MIN(elev/(EARTH_R/2), 30);
95 guint kv = event->keyval;
97 if (kv == GDK_Left || kv == GDK_h) gis_viewer_pan(viewer, 0, -pan, 0);
98 else if (kv == GDK_Down || kv == GDK_j) gis_viewer_pan(viewer, -pan, 0, 0);
99 else if (kv == GDK_Up || kv == GDK_k) gis_viewer_pan(viewer, pan, 0, 0);
100 else if (kv == GDK_Right || kv == GDK_l) gis_viewer_pan(viewer, 0, pan, 0);
101 else if (kv == GDK_minus || kv == GDK_o) gis_viewer_zoom(viewer, 10./9);
102 else if (kv == GDK_plus || kv == GDK_i) gis_viewer_zoom(viewer, 9./10);
103 else if (kv == GDK_H) gis_viewer_rotate(viewer, 0, 0, -2);
104 else if (kv == GDK_J) gis_viewer_rotate(viewer, 2, 0, 0);
105 else if (kv == GDK_K) gis_viewer_rotate(viewer, -2, 0, 0);
106 else if (kv == GDK_L) gis_viewer_rotate(viewer, 0, 0, 2);
117 static gboolean on_button_press(GisViewer *viewer, GdkEventButton *event, gpointer _)
119 g_debug("GisViewer: on_button_press - %d", event->button);
120 gtk_widget_grab_focus(GTK_WIDGET(viewer));
121 switch (event->button) {
122 case 1: viewer->drag_mode = GIS_DRAG_PAN; break;
123 case 2: viewer->drag_mode = GIS_DRAG_ZOOM; break;
124 case 3: viewer->drag_mode = GIS_DRAG_TILT; break;
125 defualt: viewer->drag_mode = GIS_DRAG_NONE; break;
127 viewer->drag_x = event->x;
128 viewer->drag_y = event->y;
132 static gboolean on_button_release(GisViewer *viewer, GdkEventButton *event, gpointer _)
134 g_debug("GisViewer: on_button_release");
135 viewer->drag_mode = GIS_DRAG_NONE;
139 static gboolean on_motion_notify(GisViewer *viewer, GdkEventMotion *event, gpointer _)
141 gdouble x_dist = viewer->drag_x - event->x;
142 gdouble y_dist = viewer->drag_y - event->y;
143 gdouble lat, lon, elev, scale;
144 gis_viewer_get_location(GIS_VIEWER(viewer), &lat, &lon, &elev);
145 scale = elev/EARTH_R/15;
146 switch (viewer->drag_mode) {
148 gis_viewer_pan(viewer, -y_dist*scale, x_dist*scale, 0);
151 gis_viewer_zoom(viewer, pow(2, -y_dist/500));
154 gis_viewer_rotate(viewer, y_dist/10, 0, x_dist/10);
157 viewer->drag_x = event->x;
158 viewer->drag_y = event->y;
162 static void on_view_changed(GisViewer *viewer,
163 gdouble _1, gdouble _2, gdouble _3)
165 gtk_widget_queue_draw(GTK_WIDGET(viewer));
171 void gis_viewer_setup(GisViewer *viewer, GisPlugins *plugins, GisPrefs *prefs)
173 viewer->plugins = plugins;
174 viewer->prefs = prefs;
175 viewer->offline = gis_prefs_get_boolean(prefs, "gis/offline", NULL);
178 void gis_viewer_set_time(GisViewer *viewer, const char *time)
180 g_assert(GIS_IS_VIEWER(viewer));
181 g_debug("GisViewer: set_time - time=%s", time);
182 g_free(viewer->time);
183 viewer->time = g_strdup(time);
184 _gis_viewer_emit_time_changed(viewer);
187 gchar *gis_viewer_get_time(GisViewer *viewer)
189 g_assert(GIS_IS_VIEWER(viewer));
190 g_debug("GisViewer: get_time");
194 void gis_viewer_set_location(GisViewer *viewer, gdouble lat, gdouble lon, gdouble elev)
196 g_assert(GIS_IS_VIEWER(viewer));
197 g_debug("GisViewer: set_location");
198 viewer->location[0] = lat;
199 viewer->location[1] = lon;
200 viewer->location[2] = elev;
201 _gis_viewer_fix_location(viewer);
202 _gis_viewer_emit_location_changed(viewer);
205 void gis_viewer_get_location(GisViewer *viewer, gdouble *lat, gdouble *lon, gdouble *elev)
207 g_assert(GIS_IS_VIEWER(viewer));
208 //g_debug("GisViewer: get_location");
209 *lat = viewer->location[0];
210 *lon = viewer->location[1];
211 *elev = viewer->location[2];
214 void gis_viewer_pan(GisViewer *viewer, gdouble forward, gdouble sideways, gdouble up)
216 g_assert(GIS_IS_VIEWER(viewer));
217 g_debug("GisViewer: pan - forward=%8.3f, sideways=%8.3f, up=%8.3f",
218 forward, sideways, up);
219 gdouble dist = sqrt(forward*forward + sideways*sideways);
220 gdouble angle1 = deg2rad(viewer->rotation[2]);
221 gdouble angle2 = atan2(sideways, forward);
222 gdouble angle = angle1 + angle2;
223 g_message("pan: dist=%f, angle=%f+%f=%f move=%f,%f",
224 dist, angle1, angle2, angle,
227 /* This isn't accurate, but it's usable */
228 viewer->location[0] += dist*cos(angle);
229 viewer->location[1] += dist*sin(angle);
230 viewer->location[2] += up;
231 _gis_viewer_fix_location(viewer);
232 _gis_viewer_emit_location_changed(viewer);
235 void gis_viewer_zoom(GisViewer *viewer, gdouble scale)
237 g_assert(GIS_IS_VIEWER(viewer));
238 g_debug("GisViewer: zoom");
239 viewer->location[2] *= scale;
240 _gis_viewer_emit_location_changed(viewer);
243 void gis_viewer_set_rotation(GisViewer *viewer, gdouble x, gdouble y, gdouble z)
245 g_assert(GIS_IS_VIEWER(viewer));
246 g_debug("GisViewer: set_rotation");
247 viewer->rotation[0] = x;
248 viewer->rotation[1] = y;
249 viewer->rotation[2] = z;
250 _gis_viewer_emit_rotation_changed(viewer);
253 void gis_viewer_get_rotation(GisViewer *viewer, gdouble *x, gdouble *y, gdouble *z)
255 g_assert(GIS_IS_VIEWER(viewer));
256 g_debug("GisViewer: get_rotation");
257 *x = viewer->rotation[0];
258 *y = viewer->rotation[1];
259 *z = viewer->rotation[2];
262 void gis_viewer_rotate(GisViewer *viewer, gdouble x, gdouble y, gdouble z)
264 g_assert(GIS_IS_VIEWER(viewer));
265 g_debug("GisViewer: rotate - x=%.0f, y=%.0f, z=%.0f", x, y, z);
266 viewer->rotation[0] += x;
267 viewer->rotation[1] += y;
268 viewer->rotation[2] += z;
269 _gis_viewer_emit_rotation_changed(viewer);
272 void gis_viewer_refresh(GisViewer *viewer)
274 g_debug("GisViewer: refresh");
275 _gis_viewer_emit_refresh(viewer);
278 void gis_viewer_set_offline(GisViewer *viewer, gboolean offline)
280 g_assert(GIS_IS_VIEWER(viewer));
281 g_debug("GisViewer: set_offline - %d", offline);
282 gis_prefs_set_boolean(viewer->prefs, "gis/offline", offline);
283 viewer->offline = offline;
284 _gis_viewer_emit_offline(viewer);
287 gboolean gis_viewer_get_offline(GisViewer *viewer)
289 g_assert(GIS_IS_VIEWER(viewer));
290 g_debug("GisViewer: get_offline - %d", viewer->offline);
291 return viewer->offline;
294 /* To be implemented by subclasses */
295 void gis_viewer_center_position(GisViewer *viewer,
296 gdouble lat, gdouble lon, gdouble elev)
298 GisViewerClass *klass = GIS_VIEWER_GET_CLASS(viewer);
299 if (!klass->center_position)
300 g_warning("GisViewer: center_position - Unimplemented");
301 klass->center_position(viewer, lat, lon, elev);
304 void gis_viewer_project(GisViewer *viewer,
305 gdouble lat, gdouble lon, gdouble elev,
306 gdouble *px, gdouble *py, gdouble *pz)
308 GisViewerClass *klass = GIS_VIEWER_GET_CLASS(viewer);
310 g_warning("GisViewer: project - Unimplemented");
311 klass->project(viewer, lat, lon, elev, px, py, pz);
314 void gis_viewer_clear_height_func(GisViewer *viewer)
316 GisViewerClass *klass = GIS_VIEWER_GET_CLASS(viewer);
317 if (!klass->clear_height_func)
318 g_warning("GisViewer: clear_height_func - Unimplemented");
319 klass->clear_height_func(viewer);
322 void gis_viewer_set_height_func(GisViewer *viewer, GisTile *tile,
323 GisHeightFunc height_func, gpointer user_data,
326 GisViewerClass *klass = GIS_VIEWER_GET_CLASS(viewer);
327 if (!klass->set_height_func)
328 g_warning("GisViewer: set_height_func - Unimplemented");
329 klass->set_height_func(viewer, tile, height_func, user_data, update);
332 gpointer gis_viewer_add(GisViewer *viewer, GisObject *object,
333 gint level, gboolean sort)
335 GisViewerClass *klass = GIS_VIEWER_GET_CLASS(viewer);
337 g_warning("GisViewer: add - Unimplemented");
338 return klass->add(viewer, object, level, sort);
341 GisObject *gis_viewer_remove(GisViewer *viewer, gpointer ref)
343 GisViewerClass *klass = GIS_VIEWER_GET_CLASS(viewer);
345 g_warning("GisViewer: remove - Unimplemented");
346 return klass->remove(viewer, ref);
352 G_DEFINE_ABSTRACT_TYPE(GisViewer, gis_viewer, GTK_TYPE_DRAWING_AREA);
353 static void gis_viewer_init(GisViewer *viewer)
355 g_debug("GisViewer: init");
357 viewer->time = g_strdup("");
358 viewer->location[0] = 40;
359 viewer->location[1] = -100;
360 viewer->location[2] = 1.5*EARTH_R;
361 viewer->rotation[0] = 0;
362 viewer->rotation[1] = 0;
363 viewer->rotation[2] = 0;
365 g_object_set(viewer, "can-focus", TRUE, NULL);
366 gtk_widget_add_events(GTK_WIDGET(viewer),
367 GDK_BUTTON_PRESS_MASK |
368 GDK_BUTTON_RELEASE_MASK |
369 GDK_POINTER_MOTION_MASK |
372 g_signal_connect(viewer, "key-press-event", G_CALLBACK(on_key_press), NULL);
374 g_signal_connect(viewer, "button-press-event", G_CALLBACK(on_button_press), NULL);
375 g_signal_connect(viewer, "button-release-event", G_CALLBACK(on_button_release), NULL);
376 g_signal_connect(viewer, "motion-notify-event", G_CALLBACK(on_motion_notify), NULL);
378 g_signal_connect(viewer, "location-changed", G_CALLBACK(on_view_changed), NULL);
379 g_signal_connect(viewer, "rotation-changed", G_CALLBACK(on_view_changed), NULL);
381 static void gis_viewer_finalize(GObject *gobject)
383 g_debug("GisViewer: finalize");
384 GisViewer *viewer = GIS_VIEWER(gobject);
385 g_free(viewer->time);
386 G_OBJECT_CLASS(gis_viewer_parent_class)->finalize(gobject);
388 static void gis_viewer_class_init(GisViewerClass *klass)
390 g_debug("GisViewer: class_init");
391 GObjectClass *gobject_class = G_OBJECT_CLASS(klass);
392 gobject_class->finalize = gis_viewer_finalize;
393 signals[SIG_TIME_CHANGED] = g_signal_new(
395 G_TYPE_FROM_CLASS(gobject_class),
400 g_cclosure_marshal_VOID__STRING,
404 signals[SIG_LOCATION_CHANGED] = g_signal_new(
406 G_TYPE_FROM_CLASS(gobject_class),
411 gis_cclosure_marshal_VOID__DOUBLE_DOUBLE_DOUBLE,
417 signals[SIG_ROTATION_CHANGED] = g_signal_new(
419 G_TYPE_FROM_CLASS(gobject_class),
424 gis_cclosure_marshal_VOID__DOUBLE_DOUBLE_DOUBLE,
430 signals[SIG_REFRESH] = g_signal_new(
432 G_TYPE_FROM_CLASS(gobject_class),
437 g_cclosure_marshal_VOID__VOID,
440 signals[SIG_OFFLINE] = g_signal_new(
442 G_TYPE_FROM_CLASS(gobject_class),
447 g_cclosure_marshal_VOID__BOOLEAN,