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");
158 /* Load OpenGL texture (from main thread) */
160 glGenTextures(1, &tdata->tex);
161 glBindTexture(GL_TEXTURE_2D, tdata->tex);
163 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
164 glPixelStorei(GL_PACK_ALIGNMENT, 1);
165 glTexImage2D(GL_TEXTURE_2D, 0, TILE_CHANNELS, TILE_WIDTH, TILE_HEIGHT, 0,
166 GL_RGBA, GL_UNSIGNED_BYTE, data->pixels);
167 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
168 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
169 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
170 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
174 /* Set hight function (from main thread) */
176 grits_viewer_set_height_func(data->elev->viewer, &data->tile->edge,
177 _height_func, data->elev, TRUE);
180 /* Queue tiles for drawing */
181 data->tile->data = tdata;
182 gtk_widget_queue_draw(GTK_WIDGET(data->elev->viewer));
185 g_free(data->pixels);
190 static void _load_tile(GritsTile *tile, gpointer _elev)
192 GritsPluginElev *elev = _elev;
194 guchar *pixels = NULL;
196 g_debug("GritsPluginElev: _load_tile start %p", g_thread_self());
198 g_debug("GritsPluginElev: _load_tile - aborted");
203 gchar *path = grits_wms_fetch(elev->wms, tile, GRITS_ONCE, NULL, NULL);
207 bil = _load_bil(path);
213 pixels = _load_pixels(bil);
217 /* Copy pixbuf data for callback */
218 struct _LoadTileData *data = g_new0(struct _LoadTileData, 1);
219 struct _TileData *tdata = g_new0(struct _TileData, 1);
222 data->pixels = pixels;
227 /* Load the GL texture from the main thread */
228 g_idle_add_full(G_PRIORITY_LOW, _load_tile_cb, data, NULL);
229 g_debug("GritsPluginElev: _load_tile end %p", g_thread_self());
232 static gboolean _free_tile_cb(gpointer _data)
234 struct _TileData *data = _data;
238 glDeleteTextures(1, &data->tex);
242 static void _free_tile(GritsTile *tile, gpointer _elev)
244 g_debug("GritsPluginElev: _free_tile: %p", tile->data);
246 g_idle_add_full(G_PRIORITY_LOW, _free_tile_cb, tile->data, NULL);
249 static void _update_tiles(gpointer _, gpointer _elev)
251 g_debug("GritsPluginElev: _update_tiles");
252 GritsPluginElev *elev = _elev;
254 grits_viewer_get_location(elev->viewer, &eye.lat, &eye.lon, &eye.elev);
255 grits_tile_update(elev->tiles, &eye,
256 MAX_RESOLUTION, TILE_WIDTH, TILE_WIDTH,
258 grits_tile_gc(elev->tiles, time(NULL)-10,
265 static void _on_location_changed(GritsViewer *viewer,
266 gdouble lat, gdouble lon, gdouble elevation, GritsPluginElev *elev)
268 g_thread_pool_push(elev->threads, NULL+1, NULL);
275 * grits_plugin_elev_new:
276 * @viewer: the #GritsViewer to use for drawing
278 * Create a new instance of the elevation plugin.
280 * Returns: the new #GritsPluginElev
282 GritsPluginElev *grits_plugin_elev_new(GritsViewer *viewer)
284 g_debug("GritsPluginElev: new");
285 GritsPluginElev *elev = g_object_new(GRITS_TYPE_PLUGIN_ELEV, NULL);
286 elev->viewer = g_object_ref(viewer);
288 /* Load initial tiles */
289 _update_tiles(NULL, elev);
291 /* Connect signals */
292 elev->sigid = g_signal_connect(elev->viewer, "location-changed",
293 G_CALLBACK(_on_location_changed), elev);
297 grits_viewer_add(viewer, GRITS_OBJECT(elev->tiles), GRITS_LEVEL_WORLD, FALSE);
307 static void grits_plugin_elev_plugin_init(GritsPluginInterface *iface);
308 G_DEFINE_TYPE_WITH_CODE(GritsPluginElev, grits_plugin_elev, G_TYPE_OBJECT,
309 G_IMPLEMENT_INTERFACE(GRITS_TYPE_PLUGIN,
310 grits_plugin_elev_plugin_init));
311 static void grits_plugin_elev_plugin_init(GritsPluginInterface *iface)
313 g_debug("GritsPluginElev: plugin_init");
314 /* Add methods to the interface */
316 /* Class/Object init */
317 static void grits_plugin_elev_init(GritsPluginElev *elev)
319 g_debug("GritsPluginElev: init");
321 elev->threads = g_thread_pool_new(_update_tiles, elev, 1, FALSE, NULL);
322 elev->tiles = grits_tile_new(NULL, NORTH, SOUTH, EAST, WEST);
323 elev->wms = grits_wms_new(
324 "http://www.nasa.network.com/elev", "mergedSrtm", "application/bil",
325 "srtm/", "bil", TILE_WIDTH, TILE_HEIGHT);
326 g_object_ref(elev->tiles);
328 static void grits_plugin_elev_dispose(GObject *gobject)
330 g_debug("GritsPluginElev: dispose");
331 GritsPluginElev *elev = GRITS_PLUGIN_ELEV(gobject);
332 elev->aborted = TRUE;
333 /* Drop references */
335 GritsViewer *viewer = elev->viewer;
337 g_signal_handler_disconnect(viewer, elev->sigid);
339 grits_viewer_clear_height_func(viewer);
341 grits_viewer_remove(viewer, GRITS_OBJECT(elev->tiles));
343 g_object_unref(elev->tiles);
344 soup_session_abort(elev->wms->http->soup);
345 g_thread_pool_free(elev->threads, TRUE, TRUE);
346 while (gtk_events_pending())
347 gtk_main_iteration();
348 g_object_unref(viewer);
350 G_OBJECT_CLASS(grits_plugin_elev_parent_class)->dispose(gobject);
352 static void grits_plugin_elev_finalize(GObject *gobject)
354 g_debug("GritsPluginElev: finalize");
355 GritsPluginElev *elev = GRITS_PLUGIN_ELEV(gobject);
357 grits_wms_free(elev->wms);
358 grits_tile_free(elev->tiles, _free_tile, elev);
359 G_OBJECT_CLASS(grits_plugin_elev_parent_class)->finalize(gobject);
362 static void grits_plugin_elev_class_init(GritsPluginElevClass *klass)
364 g_debug("GritsPluginElev: class_init");
365 GObjectClass *gobject_class = (GObjectClass*)klass;
366 gobject_class->dispose = grits_plugin_elev_dispose;
367 gobject_class->finalize = grits_plugin_elev_finalize;