]> Pileus Git - grits/commitdiff
Add Shuttle Radar Topography Mission plugin and height code
authorAndy Spencer <andy753421@gmail.com>
Mon, 9 Nov 2009 13:24:54 +0000 (13:24 +0000)
committerAndy Spencer <andy753421@gmail.com>
Mon, 9 Nov 2009 13:24:54 +0000 (13:24 +0000)
For the most part, the SRTM plugin is simmilar to the BMNG plugin. The
loaders are more complicated because it generates both a bil (unsigned
short array) and a pixmap from the downloaded files.

Highmaps are in two parts when set_height_func is called with
update=TRUE, the heights of all the vertices for a given tile are
updated by calling height_func and using the returned value as the new
vertex height.

In addition to updating the vertex height immediately, the height
function is also stored in each vertex and used later during the
split-merge process.

The user_data passed with set_height_func could theoretically be used to
speed searching for the correct tile during the split-merge process, but
this is currently not done. (Each call to the srtm height function does
a search for the correct tile)

src/gis-opengl.c
src/gis-opengl.h
src/gis-tile.c
src/gis-tile.h
src/gis_test.c
src/plugins/srtm.c
src/plugins/srtm.h

index 4d3b7e4b98b212552f1a151dba83ed399e78d42f..d1c845f2ba4c53c125c8723949aeddd2034041e2 100644 (file)
@@ -335,6 +335,27 @@ void gis_opengl_render_tiles(GisOpenGL *opengl, GisTile *tile)
                /* No children, render this tile */
                gis_opengl_render_tile(opengl, tile);
 }
+
+void gis_opengl_set_height_func(GisOpenGL *self, GisTile *tile,
+               RoamHeightFunc height_func, gpointer user_data, gboolean update)
+{
+       if (!tile)
+               return;
+       /* TODO: get points? */
+       GList *triangles = roam_sphere_get_intersect(self->sphere,
+                       tile->edge.n, tile->edge.s, tile->edge.e, tile->edge.w);
+       for (GList *cur = triangles; cur; cur = cur->next) {
+               RoamTriangle *tri = cur->data;
+               RoamPoint *points[] = {tri->p.l, tri->p.m, tri->p.r, tri->split};
+               for (int i = 0; i < G_N_ELEMENTS(points); i++) {
+                       points[i]->height_func = height_func;
+                       points[i]->height_data = user_data;
+                       roam_point_update_height(points[i]);
+               }
+       }
+       g_list_free(triangles);
+}
+
 void gis_opengl_redraw(GisOpenGL *self)
 {
        g_debug("GisOpenGL: redraw");
index 19feefce186b5aa616d1d49f0153a2bd6642069b..37099a7d93b873d28c295fd0ad2b024076818b1d 100644 (file)
@@ -71,6 +71,9 @@ void gis_opengl_render_tile(GisOpenGL *opengl, GisTile *tile);
 
 void gis_opengl_render_tiles(GisOpenGL *opengl, GisTile *root);
 
+void gis_opengl_set_height_func(GisOpenGL *self, GisTile *tile,
+               RoamHeightFunc height_func, gpointer user_data, gboolean update);
+
 void gis_opengl_redraw(GisOpenGL *opengl);
 
 void gis_opengl_begin(GisOpenGL *opengl);
index 80772e67909d106786f6a6a79267f79d49c76884..7a0f0b832851c8e8d91535ad4844be651dfe4d6d 100644 (file)
@@ -128,6 +128,28 @@ void gis_tile_update(GisTile *self,
        }
 }
 
+GisTile *gis_tile_find(GisTile *self, gdouble lat, gdouble lon)
+{
+       gint    rows = G_N_ELEMENTS(self->children);
+       gint    cols = G_N_ELEMENTS(self->children[0]);
+
+       gdouble lat_step = (self->edge.n - self->edge.s) / rows;
+       gdouble lon_step = (self->edge.e - self->edge.w) / cols;
+
+       gdouble lat_offset = self->edge.n - lat;;
+       gdouble lon_offset = lon - self->edge.w;
+
+       gint    row = lat_offset / lat_step;
+       gint    col = lon_offset / lon_step;
+
+       if (row < 0 || row >= rows || col < 0 || col >= cols)
+               return NULL;
+       else if (self->children[row][col] && self->children[row][col]->data)
+               return gis_tile_find(self->children[row][col], lat, lon);
+       else
+               return self;
+}
+
 GisTile *gis_tile_gc(GisTile *self, time_t atime,
                GisTileFreeFunc free_func, gpointer user_data)
 {
index cda4442be6de3450be53b4da2ec638f41e4c6650..4db4f4bace9867ea426c2ef2ef469e34e4a7dd2e 100644 (file)
@@ -69,6 +69,9 @@ void gis_tile_update(GisTile *root,
                gdouble lat, gdouble lon, gdouble elev,
                GisTileLoadFunc load_func, gpointer user_data);
 
+/* Find the leaf tile containing lat-lon */
+GisTile *gis_tile_find(GisTile *root, gdouble lat, gdouble lon);
+
 /* Delete nodes that haven't been accessed since atime */
 GisTile *gis_tile_gc(GisTile *root, time_t atime,
                GisTileFreeFunc free_func, gpointer user_data);
index 35f2901546d44ae225ff32bce86996004b8f1f6e..df2692f3333f8096ce55c7ce27045e5d83be0a5a 100644 (file)
@@ -56,8 +56,8 @@ int main(int argc, char **argv)
        g_signal_connect(window,  "key-press-event", G_CALLBACK(on_key_press),  NULL);
        gtk_container_add(GTK_CONTAINER(window), GTK_WIDGET(opengl));
        gtk_widget_show_all(window);
-       gis_plugins_load(plugins, "bmng", world, view, opengl, prefs);
-       //gis_plugins_load(plugins, "srtm", world, view, opengl, prefs);
+       //gis_plugins_load(plugins, "bmng", world, view, opengl, prefs);
+       gis_plugins_load(plugins, "srtm", world, view, opengl, prefs);
 
        gis_view_set_site(view, "KLSX");
 
index 3beb2bc806acc0e4cd86ba74183457de30a44aa7..6960241b10a0e6f1bd9f2050c46d6b0f01b7958c 100644 (file)
 
 #include "srtm.h"
 
-/***********
- * Helpers *
- ***********/
-static gboolean rotate(gpointer _self)
+#define MAX_RESOLUTION 500
+#define TILE_WIDTH     1024
+#define TILE_HEIGHT    512
+
+struct _TileData {
+       /* OpenGL has to be first to make gis_opengl_render_tiles happy */
+       guint      opengl;
+       guint16   *bil;
+};
+
+static gdouble _height_func(gdouble lat, gdouble lon, gpointer _self)
 {
        GisPluginSrtm *self = _self;
-       if (gtk_toggle_button_get_active(self->button)) {
-               self->rotation += 1.0;
-               gis_opengl_redraw(self->opengl);
+       if (!self) return 0;
+
+       GisTile *tile = gis_tile_find(self->tiles, lat, lon);
+       if (!tile) return 0;
+
+       struct _TileData *data = tile->data;
+       if (!data) return 0;
+
+       guint16 *bil  = data->bil;
+       if (!bil)  return 0;
+
+       gint w = TILE_WIDTH;
+       gint h = TILE_HEIGHT;
+
+       gdouble ymin  = tile->edge.s;
+       gdouble ymax  = tile->edge.n;
+       gdouble xmin  = tile->edge.w;
+       gdouble xmax  = tile->edge.e;
+
+       gdouble xdist = xmax - xmin;
+       gdouble ydist = ymax - ymin;
+
+       gdouble x =    (lon-xmin)/xdist  * w;
+       gdouble y = (1-(lat-ymin)/ydist) * h;
+
+       gdouble x_rem = x - (int)x;
+       gdouble y_rem = y - (int)y;
+       guint x_flr = (int)x;
+       guint y_flr = (int)y;
+
+       /* TODO: Fix interpolation at edges:
+        *   - Pad these at the edges instead of wrapping/truncating
+        *   - Figure out which pixels to index (is 0,0 edge, center, etc) */
+       gint16 px00 = bil[MIN((y_flr  ),h-1)*w + MIN((x_flr  ),w-1)];
+       gint16 px10 = bil[MIN((y_flr  ),h-1)*w + MIN((x_flr+1),w-1)];
+       gint16 px01 = bil[MIN((y_flr+1),h-1)*w + MIN((x_flr  ),w-1)];
+       gint16 px11 = bil[MIN((y_flr+1),h-1)*w + MIN((x_flr+1),w-1)];
+
+       gdouble elev =
+               px00 * (1-x_rem) * (1-y_rem) +
+               px10 * (  x_rem) * (1-y_rem) +
+               px01 * (1-x_rem) * (  y_rem) +
+               px11 * (  x_rem) * (  y_rem);
+       return elev;
+}
+
+/**********************
+ * Loader and Freeers *
+ **********************/
+#define LOAD_BIL    TRUE
+#define LOAD_OPENGL TRUE
+static guint16 *_load_bil(gchar *path)
+{
+       gchar *data;
+       g_file_get_contents(path, &data, NULL, NULL);
+       g_debug("GisPluginSrtm: load_bil %p", data);
+       return (guint16*)data;
+}
+static GdkPixbuf *_load_pixbuf(guint16 *bil)
+{
+       GdkPixbuf *pixbuf = gdk_pixbuf_new(GDK_COLORSPACE_RGB, FALSE, 8, TILE_WIDTH, TILE_HEIGHT);
+       guchar    *pixels = gdk_pixbuf_get_pixels(pixbuf);
+       gint       stride = gdk_pixbuf_get_rowstride(pixbuf);
+       gint       nchan  = gdk_pixbuf_get_n_channels(pixbuf);
+
+       for (int r = 0; r < TILE_HEIGHT; r++) {
+               for (int c = 0; c < TILE_WIDTH; c++) {
+                       gint16 value = bil[r*TILE_WIDTH + c];
+                       //guchar color = (float)(MAX(value,0))/8848 * 255;
+                       guchar color = (float)value/8848 * 255;
+                       pixels[r*stride + c*nchan + 0] = color;
+                       pixels[r*stride + c*nchan + 1] = color;
+                       pixels[r*stride + c*nchan + 2] = color;
+                       if (nchan == 4)
+                               pixels[r*stride + c*nchan + 3] = 128;
+               }
        }
-       return TRUE;
+       g_debug("GisPluginSrtm: load_pixbuf %p", pixbuf);
+       return pixbuf;
 }
+static guint _load_opengl(GdkPixbuf *pixbuf)
+{
+       /* Load image */
+       guchar *pixels = gdk_pixbuf_get_pixels(pixbuf);
+       gint    alpha  = gdk_pixbuf_get_has_alpha(pixbuf);
+       gint    nchan  = 4; // gdk_pixbuf_get_n_channels(pixbuf);
+       gint    width  = gdk_pixbuf_get_width(pixbuf);
+       gint    height = gdk_pixbuf_get_height(pixbuf);
+
+       /* Create Texture */
+       guint opengl;
+       glGenTextures(1, &opengl);
+       glBindTexture(GL_TEXTURE_2D, opengl);
 
+       glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
+       glPixelStorei(GL_PACK_ALIGNMENT, 1);
+       glTexImage2D(GL_TEXTURE_2D, 0, nchan, width, height, 0,
+                       (alpha ? GL_RGBA : GL_RGB), GL_UNSIGNED_BYTE, pixels);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
+       glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_BORDER);
+       glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_BORDER);
+
+       g_debug("GisPluginSrtm: load_opengl %d", opengl);
+       return opengl;
+}
+static void _load_tile(GisTile *tile, gpointer _self)
+{
+       GisPluginSrtm *self = _self;
+       g_debug("GisPluginSrtm: _load_tile");
+
+       struct _TileData *data = g_new0(struct _TileData, 1);
+       gchar *path = gis_wms_make_local(self->wms, tile);
+       if (LOAD_BIL || LOAD_OPENGL)
+               data->bil = _load_bil(path);
+       g_free(path);
+       if (LOAD_OPENGL) {
+               GdkPixbuf *pixbuf = _load_pixbuf(data->bil);
+               data->opengl = _load_opengl(pixbuf);
+               g_object_unref(pixbuf);
+       }
+
+       /* Do necessasairy processing */
+       if (LOAD_BIL)
+               gis_opengl_set_height_func(self->opengl, tile,
+                       _height_func, self, TRUE);
+
+       /* Cleanup unneeded things */
+       if (!LOAD_BIL)
+               g_free(data->bil);
+
+       tile->data = data;
+}
+
+static void _free_tile(GisTile *tile, gpointer _self)
+{
+       GisPluginSrtm *self = _self;
+       struct _TileData *data = tile->data;
+       g_debug("GisPluginSrtm: _free_tile: %p=%d", data, data->opengl);
+       if (LOAD_BIL)
+               g_free(data->bil);
+       if (LOAD_OPENGL)
+               glDeleteTextures(1, &data->opengl);
+       g_free(data);
+}
+
+static gpointer _update_tiles(gpointer _self)
+{
+       GisPluginSrtm *self = _self;
+       gdouble lat, lon, elev;
+       gis_view_get_location(self->view, &lat, &lon, &elev);
+       gis_tile_update(self->tiles,
+                       MAX_RESOLUTION, TILE_WIDTH, TILE_WIDTH,
+                       lat, lon, elev,
+                       _load_tile, self);
+       gis_tile_gc(self->tiles, time(NULL)-10,
+                       _free_tile, self);
+       return NULL;
+}
+
+/*************
+ * Callbacks *
+ *************/
+static void _on_location_changed(GisView *view, gdouble lat, gdouble lon, gdouble elev,
+               GisPluginSrtm *self)
+{
+       _update_tiles(self);
+}
 
 /***********
  * Methods *
@@ -43,42 +211,26 @@ GisPluginSrtm *gis_plugin_srtm_new(GisWorld *world, GisView *view, GisOpenGL *op
 {
        g_debug("GisPluginSrtm: new");
        GisPluginSrtm *self = g_object_new(GIS_TYPE_PLUGIN_SRTM, NULL);
+       self->view   = view;
        self->opengl = opengl;
 
-       return self;
-}
+       /* Load initial tiles */
+       _load_tile(self->tiles, self);
+       _update_tiles(self);
 
-static GtkWidget *gis_plugin_srtm_get_config(GisPlugin *_self)
-{
-       GisPluginSrtm *self = GIS_PLUGIN_SRTM(_self);
-       return GTK_WIDGET(self->button);
+       /* Connect signals */
+       g_signal_connect(view, "location-changed", G_CALLBACK(_on_location_changed), self);
+
+       return self;
 }
 
 static void gis_plugin_srtm_expose(GisPlugin *_self)
 {
        GisPluginSrtm *self = GIS_PLUGIN_SRTM(_self);
-       g_debug("GisPluginSrtm: expose");
-
-       glMatrixMode(GL_PROJECTION);
-       glLoadIdentity();
-       glOrtho(1,-1, -1,1, -10,10);
-
-       glMatrixMode(GL_MODELVIEW);
-       glLoadIdentity();
-
-       float light_ambient[]  = {0.1f, 0.1f, 0.0f, 1.0f};
-       float light_diffuse[]  = {0.9f, 0.9f, 0.9f, 1.0f};
-       float light_position[] = {-30.0f, 50.0f, 40.0f, 1.0f};
-       glLightfv(GL_LIGHT0, GL_AMBIENT,  light_ambient);
-       glLightfv(GL_LIGHT0, GL_DIFFUSE,  light_diffuse);
-       glLightfv(GL_LIGHT0, GL_POSITION, light_position);
-       glEnable(GL_COLOR_MATERIAL);
-
-       glTranslatef(0.5, -0.5, -2);
-       glRotatef(self->rotation, 1, 1, 0);
-       glColor4f(0.9, 0.9, 0.7, 1.0);
-       glDisable(GL_CULL_FACE);
-       gdk_gl_draw_teapot(TRUE, 0.25);
+       g_debug("GisPluginSrtm: expose tiles=%p data=%p",
+               self->tiles, self->tiles->data);
+       if (LOAD_OPENGL)
+               gis_opengl_render_tiles(self->opengl, self->tiles);
 }
 
 
@@ -94,24 +246,22 @@ static void gis_plugin_srtm_plugin_init(GisPluginInterface *iface)
 {
        g_debug("GisPluginSrtm: plugin_init");
        /* Add methods to the interface */
-       iface->expose     = gis_plugin_srtm_expose;
-       iface->get_config = gis_plugin_srtm_get_config;
+       iface->expose = gis_plugin_srtm_expose;
 }
 /* Class/Object init */
 static void gis_plugin_srtm_init(GisPluginSrtm *self)
 {
        g_debug("GisPluginSrtm: init");
        /* Set defaults */
-       self->button    = GTK_TOGGLE_BUTTON(gtk_toggle_button_new_with_label("Rotate"));
-       self->rotate_id = g_timeout_add(1000/60, rotate, self);
-       self->rotation  = 30.0;
-       self->opengl    = NULL;
+       self->tiles = gis_tile_new(NULL, NORTH, SOUTH, EAST, WEST);
+       self->wms   = gis_wms_new(
+               "http://www.nasa.network.com/elev", "srtm30", "application/bil",
+               "srtm", ".bil", TILE_WIDTH, TILE_HEIGHT);
 }
 static void gis_plugin_srtm_dispose(GObject *gobject)
 {
        g_debug("GisPluginSrtm: dispose");
        GisPluginSrtm *self = GIS_PLUGIN_SRTM(gobject);
-       g_source_remove(self->rotate_id);
        /* Drop references */
        G_OBJECT_CLASS(gis_plugin_srtm_parent_class)->dispose(gobject);
 }
@@ -120,6 +270,8 @@ static void gis_plugin_srtm_finalize(GObject *gobject)
        g_debug("GisPluginSrtm: finalize");
        GisPluginSrtm *self = GIS_PLUGIN_SRTM(gobject);
        /* Free data */
+       gis_tile_free(self->tiles, _free_tile, self);
+       gis_wms_free(self->wms);
        G_OBJECT_CLASS(gis_plugin_srtm_parent_class)->finalize(gobject);
 
 }
index 895378890c8cece3cfd1239d4b9edb8d938a338a..7af659edf567a1f44b0433626e3836c1fd3c6e65 100644 (file)
@@ -34,10 +34,10 @@ struct _GisPluginSrtm {
        GObject parent_instance;
 
        /* instance members */
-       GtkToggleButton *button;
-       guint            rotate_id;
-       float            rotation;
-       GisOpenGL       *opengl;
+       GisView   *view;
+       GisOpenGL *opengl;
+       GisTile   *tiles;
+       GisWms    *wms;
 };
 
 struct _GisPluginSrtmClass {