2 * Copyright (C) 2009-2011 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/>.
20 * @short_description: Elevation plugin
22 * #GritsPluginElev provides access to ground elevation. It does this in two ways:
23 * First, it provides a height function used by the viewer when drawing the
24 * world. Second, it can load the elevation data into an image and draw a
25 * greyscale elevation overlay on the planets surface.
29 #include <glib/gstdio.h>
37 #define LOAD_TEX FALSE
39 /* Tile size constnats */
40 #define MAX_RESOLUTION 50
41 #define TILE_WIDTH 1024
42 #define TILE_HEIGHT 512
43 #define TILE_CHANNELS 4
44 #define TILE_SIZE (TILE_WIDTH*TILE_HEIGHT*sizeof(guint16))
47 /* OpenGL has to be first to make grits_tile_draw happy */
52 static gdouble _height_func(gdouble lat, gdouble lon, gpointer _elev)
54 GritsPluginElev *elev = _elev;
57 GritsTile *tile = grits_tile_find(elev->tiles, lat, lon);
60 struct _TileData *data = tile->data;
63 guint16 *bil = data->bil;
69 gdouble ymin = tile->edge.s;
70 gdouble ymax = tile->edge.n;
71 gdouble xmin = tile->edge.w;
72 gdouble xmax = tile->edge.e;
74 gdouble xdist = xmax - xmin;
75 gdouble ydist = ymax - ymin;
77 gdouble x = (lon-xmin)/xdist * w;
78 gdouble y = (1-(lat-ymin)/ydist) * h;
80 gdouble x_rem = x - (int)x;
81 gdouble y_rem = y - (int)y;
85 //if (lon == 180 || lon == -180)
86 // g_message("lon=%f w=%d min=%f max=%f dist=%f x=%f rem=%f flr=%d",
87 // lon, w, xmin, xmax, xdist, x, x_rem, x_flr);
89 /* TODO: Fix interpolation at edges:
90 * - Pad these at the edges instead of wrapping/truncating
91 * - Figure out which pixels to index (is 0,0 edge, center, etc) */
92 gint16 px00 = bil[MIN((y_flr ),h-1)*w + MIN((x_flr ),w-1)];
93 gint16 px10 = bil[MIN((y_flr ),h-1)*w + MIN((x_flr+1),w-1)];
94 gint16 px01 = bil[MIN((y_flr+1),h-1)*w + MIN((x_flr ),w-1)];
95 gint16 px11 = bil[MIN((y_flr+1),h-1)*w + MIN((x_flr+1),w-1)];
97 return px00 * (1-x_rem) * (1-y_rem) +
98 px10 * ( x_rem) * (1-y_rem) +
99 px01 * (1-x_rem) * ( y_rem) +
100 px11 * ( x_rem) * ( y_rem);
103 /**********************
104 * Loader and Freeers *
105 **********************/
107 struct _LoadTileData {
108 GritsPluginElev *elev;
111 struct _TileData *tdata;
114 static guint16 *_load_bil(gchar *path)
118 g_file_get_contents(path, &data, &len, NULL);
119 g_debug("GritsPluginElev: load_bil %p", data);
120 if (len != TILE_SIZE) {
121 g_warning("GritsPluginElev: _load_bil - unexpected tile size %ld, != %ld",
122 (glong)len, (glong)TILE_SIZE);
126 return (guint16*)data;
129 static guchar *_load_pixels(guint16 *bil)
131 g_assert(TILE_CHANNELS == 4);
133 guchar (*pixels)[TILE_WIDTH][TILE_CHANNELS]
134 = g_malloc0(TILE_HEIGHT * TILE_WIDTH * TILE_CHANNELS);
136 for (int r = 0; r < TILE_HEIGHT; r++) {
137 for (int c = 0; c < TILE_WIDTH; c++) {
138 gint16 value = bil[r*TILE_WIDTH + c];
139 guchar color = (float)value/8848 * 255;
140 //guchar color = (float)(MAX(value,0))/8848 * 255;
141 pixels[r][c][0] = color;
142 pixels[r][c][1] = color;
143 pixels[r][c][2] = color;
144 pixels[r][c][3] = 0xff;
148 g_debug("GritsPluginElev: load_pixels %p", pixels);
149 return (guchar*)pixels;
152 static gboolean _load_tile_cb(gpointer _data)
154 struct _LoadTileData *data = _data;
155 struct _TileData *tdata = data->tdata;
156 g_debug("GritsPluginElev: _load_tile_cb start");
157 if (data->elev->aborted) {
158 g_debug("GritsPluginElev: _load_tile - aborted");
162 /* Load OpenGL texture (from main thread) */
164 glGenTextures(1, &tdata->tex);
165 glBindTexture(GL_TEXTURE_2D, tdata->tex);
167 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
168 glPixelStorei(GL_PACK_ALIGNMENT, 1);
169 glTexImage2D(GL_TEXTURE_2D, 0, TILE_CHANNELS, TILE_WIDTH, TILE_HEIGHT, 0,
170 GL_RGBA, GL_UNSIGNED_BYTE, data->pixels);
171 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
172 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
173 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
174 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
178 /* Set hight function (from main thread) */
180 grits_viewer_set_height_func(data->elev->viewer, &data->tile->edge,
181 _height_func, data->elev, TRUE);
184 /* Queue tiles for drawing */
185 data->tile->data = tdata;
186 gtk_widget_queue_draw(GTK_WIDGET(data->elev->viewer));
189 g_free(data->pixels);
194 static void _load_tile(GritsTile *tile, gpointer _elev)
196 GritsPluginElev *elev = _elev;
198 guchar *pixels = NULL;
200 g_debug("GritsPluginElev: _load_tile start %p", g_thread_self());
202 g_debug("GritsPluginElev: _load_tile - aborted");
207 gchar *path = grits_wms_fetch(elev->wms, tile, GRITS_ONCE, NULL, NULL);
211 bil = _load_bil(path);
217 pixels = _load_pixels(bil);
221 /* Copy pixbuf data for callback */
222 struct _LoadTileData *data = g_new0(struct _LoadTileData, 1);
223 struct _TileData *tdata = g_new0(struct _TileData, 1);
226 data->pixels = pixels;
231 /* Load the GL texture from the main thread */
232 g_idle_add_full(G_PRIORITY_LOW, _load_tile_cb, data, NULL);
233 g_debug("GritsPluginElev: _load_tile end %p", g_thread_self());
236 static gboolean _free_tile_cb(gpointer _data)
238 struct _TileData *data = _data;
242 glDeleteTextures(1, &data->tex);
246 static void _free_tile(GritsTile *tile, gpointer _elev)
248 g_debug("GritsPluginElev: _free_tile: %p", tile->data);
250 g_idle_add_full(G_PRIORITY_LOW, _free_tile_cb, tile->data, NULL);
253 static void _update_tiles(gpointer _, gpointer _elev)
255 g_debug("GritsPluginElev: _update_tiles");
256 GritsPluginElev *elev = _elev;
258 grits_viewer_get_location(elev->viewer, &eye.lat, &eye.lon, &eye.elev);
259 grits_tile_update(elev->tiles, &eye,
260 MAX_RESOLUTION, TILE_WIDTH, TILE_WIDTH,
262 grits_tile_gc(elev->tiles, time(NULL)-10,
269 static void _on_location_changed(GritsViewer *viewer,
270 gdouble lat, gdouble lon, gdouble elevation, GritsPluginElev *elev)
272 g_thread_pool_push(elev->threads, NULL+1, NULL);
279 * grits_plugin_elev_new:
280 * @viewer: the #GritsViewer to use for drawing
282 * Create a new instance of the elevation plugin.
284 * Returns: the new #GritsPluginElev
286 GritsPluginElev *grits_plugin_elev_new(GritsViewer *viewer)
288 g_debug("GritsPluginElev: new");
289 GritsPluginElev *elev = g_object_new(GRITS_TYPE_PLUGIN_ELEV, NULL);
290 elev->viewer = g_object_ref(viewer);
292 /* Load initial tiles */
293 _update_tiles(NULL, elev);
295 /* Connect signals */
296 elev->sigid = g_signal_connect(elev->viewer, "location-changed",
297 G_CALLBACK(_on_location_changed), elev);
301 grits_viewer_add(viewer, GRITS_OBJECT(elev->tiles), GRITS_LEVEL_WORLD, FALSE);
311 static void grits_plugin_elev_plugin_init(GritsPluginInterface *iface);
312 G_DEFINE_TYPE_WITH_CODE(GritsPluginElev, grits_plugin_elev, G_TYPE_OBJECT,
313 G_IMPLEMENT_INTERFACE(GRITS_TYPE_PLUGIN,
314 grits_plugin_elev_plugin_init));
315 static void grits_plugin_elev_plugin_init(GritsPluginInterface *iface)
317 g_debug("GritsPluginElev: plugin_init");
318 /* Add methods to the interface */
320 /* Class/Object init */
321 static void grits_plugin_elev_init(GritsPluginElev *elev)
323 g_debug("GritsPluginElev: init");
325 elev->threads = g_thread_pool_new(_update_tiles, elev, 1, FALSE, NULL);
326 elev->tiles = grits_tile_new(NULL, NORTH, SOUTH, EAST, WEST);
327 elev->wms = grits_wms_new(
328 "http://www.nasa.network.com/elev", "mergedSrtm", "application/bil",
329 "srtm/", "bil", TILE_WIDTH, TILE_HEIGHT);
330 g_object_ref(elev->tiles);
332 static void grits_plugin_elev_dispose(GObject *gobject)
334 g_debug("GritsPluginElev: dispose");
335 GritsPluginElev *elev = GRITS_PLUGIN_ELEV(gobject);
336 elev->aborted = TRUE;
337 /* Drop references */
339 GritsViewer *viewer = elev->viewer;
341 g_signal_handler_disconnect(viewer, elev->sigid);
343 grits_viewer_clear_height_func(viewer);
345 grits_viewer_remove(viewer, GRITS_OBJECT(elev->tiles));
347 g_object_unref(elev->tiles);
348 soup_session_abort(elev->wms->http->soup);
349 g_thread_pool_free(elev->threads, TRUE, TRUE);
350 while (gtk_events_pending())
351 gtk_main_iteration();
352 g_object_unref(viewer);
354 G_OBJECT_CLASS(grits_plugin_elev_parent_class)->dispose(gobject);
356 static void grits_plugin_elev_finalize(GObject *gobject)
358 g_debug("GritsPluginElev: finalize");
359 GritsPluginElev *elev = GRITS_PLUGIN_ELEV(gobject);
361 grits_wms_free(elev->wms);
362 grits_tile_free(elev->tiles, _free_tile, elev);
363 G_OBJECT_CLASS(grits_plugin_elev_parent_class)->finalize(gobject);
366 static void grits_plugin_elev_class_init(GritsPluginElevClass *klass)
368 g_debug("GritsPluginElev: class_init");
369 GObjectClass *gobject_class = (GObjectClass*)klass;
370 gobject_class->dispose = grits_plugin_elev_dispose;
371 gobject_class->finalize = grits_plugin_elev_finalize;