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))
46 static gdouble _height_func(gdouble lat, gdouble lon, gpointer _elev)
48 GritsPluginElev *elev = _elev;
51 GritsTile *tile = grits_tile_find(elev->tiles, lat, lon);
54 guint16 *bil = tile->data;
60 gdouble ymin = tile->edge.s;
61 gdouble ymax = tile->edge.n;
62 gdouble xmin = tile->edge.w;
63 gdouble xmax = tile->edge.e;
65 gdouble xdist = xmax - xmin;
66 gdouble ydist = ymax - ymin;
68 gdouble x = (lon-xmin)/xdist * w;
69 gdouble y = (1-(lat-ymin)/ydist) * h;
71 gdouble x_rem = x - (int)x;
72 gdouble y_rem = y - (int)y;
76 //if (lon == 180 || lon == -180)
77 // g_message("lon=%f w=%d min=%f max=%f dist=%f x=%f rem=%f flr=%d",
78 // lon, w, xmin, xmax, xdist, x, x_rem, x_flr);
80 /* TODO: Fix interpolation at edges:
81 * - Pad these at the edges instead of wrapping/truncating
82 * - Figure out which pixels to index (is 0,0 edge, center, etc) */
83 gint16 px00 = bil[MIN((y_flr ),h-1)*w + MIN((x_flr ),w-1)];
84 gint16 px10 = bil[MIN((y_flr ),h-1)*w + MIN((x_flr+1),w-1)];
85 gint16 px01 = bil[MIN((y_flr+1),h-1)*w + MIN((x_flr ),w-1)];
86 gint16 px11 = bil[MIN((y_flr+1),h-1)*w + MIN((x_flr+1),w-1)];
88 return px00 * (1-x_rem) * (1-y_rem) +
89 px10 * ( x_rem) * (1-y_rem) +
90 px01 * (1-x_rem) * ( y_rem) +
91 px11 * ( x_rem) * ( y_rem);
94 /**********************
95 * Loader and Freeers *
96 **********************/
98 static guint16 *_load_bil(gchar *path)
102 g_file_get_contents(path, &data, &len, NULL);
103 g_debug("GritsPluginElev: load_bil %p", data);
104 if (len != TILE_SIZE) {
105 g_warning("GritsPluginElev: _load_bil - unexpected tile size %ld, != %ld",
106 (glong)len, (glong)TILE_SIZE);
110 return (guint16*)data;
113 static guchar *_load_pixels(guint16 *bil)
115 g_assert(TILE_CHANNELS == 4);
117 guchar (*pixels)[TILE_WIDTH][TILE_CHANNELS]
118 = g_malloc0(TILE_HEIGHT * TILE_WIDTH * TILE_CHANNELS);
120 for (int r = 0; r < TILE_HEIGHT; r++) {
121 for (int c = 0; c < TILE_WIDTH; c++) {
122 gint16 value = bil[r*TILE_WIDTH + c];
123 guchar color = (float)value/8848 * 255;
124 //guchar color = (float)(MAX(value,0))/8848 * 255;
125 pixels[r][c][0] = color;
126 pixels[r][c][1] = color;
127 pixels[r][c][2] = color;
128 pixels[r][c][3] = 0xff;
132 g_debug("GritsPluginElev: load_pixels %p", pixels);
133 return (guchar*)pixels;
136 static void _load_tile(GritsTile *tile, gpointer _elev)
138 GritsPluginElev *elev = _elev;
140 g_debug("GritsPluginElev: _load_tile start %p", g_thread_self());
142 g_debug("GritsPluginElev: _load_tile - aborted");
147 gchar *path = grits_wms_fetch(elev->wms, tile, GRITS_ONCE, NULL, NULL);
152 guint16 *bil = _load_bil(path);
157 /* Set hight function (TODO: from main thread?) */
160 grits_viewer_set_height_func(elev->viewer, &tile->edge,
161 _height_func, elev, TRUE);
164 /* Load pixels for grayscale height textures */
166 guchar *pixels = _load_pixels(bil);
167 grits_tile_load_pixels(tile, pixels,
168 TILE_WIDTH, TILE_HEIGHT, TILE_CHANNELS==4);
171 /* Free bill if we're not interested in a hight function */
175 /* Load the GL texture from the main thread */
176 g_debug("GritsPluginElev: _load_tile end %p", g_thread_self());
179 static void _update_tiles(gpointer _, gpointer _elev)
181 g_debug("GritsPluginElev: _update_tiles");
182 GritsPluginElev *elev = _elev;
184 grits_viewer_get_location(elev->viewer, &eye.lat, &eye.lon, &eye.elev);
185 grits_tile_update(elev->tiles, &eye,
186 MAX_RESOLUTION, TILE_WIDTH, TILE_WIDTH,
188 grits_tile_gc(elev->tiles, time(NULL)-10, NULL, elev);
194 static void _on_location_changed(GritsViewer *viewer,
195 gdouble lat, gdouble lon, gdouble elevation, GritsPluginElev *elev)
197 g_thread_pool_push(elev->threads, NULL+1, NULL);
204 * grits_plugin_elev_new:
205 * @viewer: the #GritsViewer to use for drawing
207 * Create a new instance of the elevation plugin.
209 * Returns: the new #GritsPluginElev
211 GritsPluginElev *grits_plugin_elev_new(GritsViewer *viewer)
213 g_debug("GritsPluginElev: new");
214 GritsPluginElev *elev = g_object_new(GRITS_TYPE_PLUGIN_ELEV, NULL);
215 elev->viewer = g_object_ref(viewer);
217 /* Load initial tiles */
218 _update_tiles(NULL, elev);
220 /* Connect signals */
221 elev->sigid = g_signal_connect(elev->viewer, "location-changed",
222 G_CALLBACK(_on_location_changed), elev);
226 grits_viewer_add(viewer, GRITS_OBJECT(elev->tiles), GRITS_LEVEL_WORLD, FALSE);
236 static void grits_plugin_elev_plugin_init(GritsPluginInterface *iface);
237 G_DEFINE_TYPE_WITH_CODE(GritsPluginElev, grits_plugin_elev, G_TYPE_OBJECT,
238 G_IMPLEMENT_INTERFACE(GRITS_TYPE_PLUGIN,
239 grits_plugin_elev_plugin_init));
240 static void grits_plugin_elev_plugin_init(GritsPluginInterface *iface)
242 g_debug("GritsPluginElev: plugin_init");
243 /* Add methods to the interface */
245 /* Class/Object init */
246 static void grits_plugin_elev_init(GritsPluginElev *elev)
248 g_debug("GritsPluginElev: init");
250 elev->threads = g_thread_pool_new(_update_tiles, elev, 1, FALSE, NULL);
251 elev->tiles = grits_tile_new(NULL, NORTH, SOUTH, EAST, WEST);
252 elev->wms = grits_wms_new(
253 "http://www.nasa.network.com/elev", "mergedSrtm", "application/bil",
254 "srtm/", "bil", TILE_WIDTH, TILE_HEIGHT);
255 g_object_ref(elev->tiles);
257 static void grits_plugin_elev_dispose(GObject *gobject)
259 g_debug("GritsPluginElev: dispose");
260 GritsPluginElev *elev = GRITS_PLUGIN_ELEV(gobject);
261 elev->aborted = TRUE;
262 /* Drop references */
264 GritsViewer *viewer = elev->viewer;
266 g_signal_handler_disconnect(viewer, elev->sigid);
268 grits_viewer_clear_height_func(viewer);
270 grits_viewer_remove(viewer, GRITS_OBJECT(elev->tiles));
271 g_object_unref(elev->tiles);
272 soup_session_abort(elev->wms->http->soup);
273 g_thread_pool_free(elev->threads, TRUE, TRUE);
274 g_object_unref(viewer);
276 G_OBJECT_CLASS(grits_plugin_elev_parent_class)->dispose(gobject);
278 static void grits_plugin_elev_finalize(GObject *gobject)
280 g_debug("GritsPluginElev: finalize");
281 GritsPluginElev *elev = GRITS_PLUGIN_ELEV(gobject);
283 grits_wms_free(elev->wms);
284 grits_tile_free(elev->tiles, NULL, elev);
285 G_OBJECT_CLASS(grits_plugin_elev_parent_class)->finalize(gobject);
288 static void grits_plugin_elev_class_init(GritsPluginElevClass *klass)
290 g_debug("GritsPluginElev: class_init");
291 GObjectClass *gobject_class = (GObjectClass*)klass;
292 gobject_class->dispose = grits_plugin_elev_dispose;
293 gobject_class->finalize = grits_plugin_elev_finalize;