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_thread(gpointer _tile, gpointer _elev)
138 GritsTile *tile = _tile;
139 GritsPluginElev *elev = _elev;
141 g_debug("GritsPluginElev: _load_tile_thread start %p - tile=%p",
142 g_thread_self(), tile);
144 g_debug("GritsPluginElev: _load_tile_thread - aborted");
149 gchar *path = grits_wms_fetch(elev->wms, tile, GRITS_ONCE, NULL, NULL);
154 guint16 *bil = _load_bil(path);
159 /* Set hight function (TODO: from main thread?) */
162 grits_viewer_set_height_func(elev->viewer, &tile->edge,
163 _height_func, elev, TRUE);
166 /* Load pixels for grayscale height textures */
168 guchar *pixels = _load_pixels(bil);
169 grits_tile_load_pixels(tile, pixels,
170 TILE_WIDTH, TILE_HEIGHT, TILE_CHANNELS==4);
173 /* Free bill if we're not interested in a hight function */
177 /* Load the GL texture from the main thread */
178 g_debug("GritsPluginElev: _load_tile_thread end %p", g_thread_self());
181 static void _load_tile_func(GritsTile *tile, gpointer _elev)
183 g_debug("GritsPluginElev: _load_tile_func - tile=%p", tile);
184 GritsPluginElev *elev = _elev;
185 g_thread_pool_push(elev->threads, tile, NULL);
191 static void _on_location_changed(GritsViewer *viewer,
192 gdouble lat, gdouble lon, gdouble elevation, GritsPluginElev *elev)
194 GritsPoint eye = {lat, lon, elevation};
195 grits_tile_update(elev->tiles, &eye,
196 MAX_RESOLUTION, TILE_WIDTH, TILE_WIDTH,
197 _load_tile_func, elev);
198 grits_tile_gc(elev->tiles, time(NULL)-10, NULL, elev);
205 * grits_plugin_elev_new:
206 * @viewer: the #GritsViewer to use for drawing
208 * Create a new instance of the elevation plugin.
210 * Returns: the new #GritsPluginElev
212 GritsPluginElev *grits_plugin_elev_new(GritsViewer *viewer)
214 g_debug("GritsPluginElev: new");
215 GritsPluginElev *elev = g_object_new(GRITS_TYPE_PLUGIN_ELEV, NULL);
216 elev->viewer = g_object_ref(viewer);
218 /* Load initial tiles */
219 gdouble lat, lon, elevation;
220 grits_viewer_get_location(viewer, &lat, &lon, &elevation);
221 _on_location_changed(viewer, lat, lon, elevation, elev);
223 /* Connect signals */
224 elev->sigid = g_signal_connect(elev->viewer, "location-changed",
225 G_CALLBACK(_on_location_changed), elev);
229 grits_viewer_add(viewer, GRITS_OBJECT(elev->tiles), GRITS_LEVEL_WORLD, FALSE);
239 static void grits_plugin_elev_plugin_init(GritsPluginInterface *iface);
240 G_DEFINE_TYPE_WITH_CODE(GritsPluginElev, grits_plugin_elev, G_TYPE_OBJECT,
241 G_IMPLEMENT_INTERFACE(GRITS_TYPE_PLUGIN,
242 grits_plugin_elev_plugin_init));
243 static void grits_plugin_elev_plugin_init(GritsPluginInterface *iface)
245 g_debug("GritsPluginElev: plugin_init");
246 /* Add methods to the interface */
248 /* Class/Object init */
249 static void grits_plugin_elev_init(GritsPluginElev *elev)
251 g_debug("GritsPluginElev: init");
253 elev->threads = g_thread_pool_new(_load_tile_thread, elev, 1, FALSE, NULL);
254 elev->tiles = grits_tile_new(NULL, NORTH, SOUTH, EAST, WEST);
255 elev->wms = grits_wms_new(
256 "http://www.nasa.network.com/elev", "mergedSrtm", "application/bil",
257 "srtm/", "bil", TILE_WIDTH, TILE_HEIGHT);
258 g_object_ref(elev->tiles);
260 static void grits_plugin_elev_dispose(GObject *gobject)
262 g_debug("GritsPluginElev: dispose");
263 GritsPluginElev *elev = GRITS_PLUGIN_ELEV(gobject);
264 elev->aborted = TRUE;
265 /* Drop references */
267 GritsViewer *viewer = elev->viewer;
268 g_signal_handler_disconnect(viewer, elev->sigid);
269 soup_session_abort(elev->wms->http->soup);
270 g_thread_pool_free(elev->threads, TRUE, TRUE);
273 grits_viewer_clear_height_func(viewer);
275 grits_viewer_remove(viewer, GRITS_OBJECT(elev->tiles));
276 g_object_unref(elev->tiles);
277 g_object_unref(viewer);
279 G_OBJECT_CLASS(grits_plugin_elev_parent_class)->dispose(gobject);
281 static void grits_plugin_elev_finalize(GObject *gobject)
283 g_debug("GritsPluginElev: finalize");
284 GritsPluginElev *elev = GRITS_PLUGIN_ELEV(gobject);
286 grits_wms_free(elev->wms);
287 grits_tile_free(elev->tiles, NULL, elev);
288 G_OBJECT_CLASS(grits_plugin_elev_parent_class)->finalize(gobject);
291 static void grits_plugin_elev_class_init(GritsPluginElevClass *klass)
293 g_debug("GritsPluginElev: class_init");
294 GObjectClass *gobject_class = (GObjectClass*)klass;
295 gobject_class->dispose = grits_plugin_elev_dispose;
296 gobject_class->finalize = grits_plugin_elev_finalize;