]> Pileus Git - aweather/blobdiff - src/plugin-radar.c
Refactoring a lot of things. Taking all the opengl and GIS stuff out of
[aweather] / src / plugin-radar.c
index 8d5fb4e3d58cd3f853a004a818e9b3771e3483e5..6ddc4028110a21f961026a44734c463af394a5fc 100644 (file)
 #include <math.h>
 #include <rsl.h>
 
+#include "misc.h"
 #include "aweather-gui.h"
 #include "plugin-radar.h"
 #include "data.h"
+#include "marching.h"
+
+static char *nexrad_base = "http://mesonet.agron.iastate.edu/data/";
 
 /****************
  * GObject code *
@@ -47,8 +51,10 @@ static void aweather_radar_init(AWeatherRadar *radar)
 {
        g_debug("AWeatherRadar: class_init");
        /* Set defaults */
-       radar->gui  = NULL;
-       radar->soup = NULL;
+       radar->gui           = NULL;
+       radar->soup          = NULL;
+       radar->cur_triangles = NULL;
+       radar->cur_num_triangles = 0;
 }
 static void aweather_radar_dispose(GObject *gobject)
 {
@@ -81,18 +87,17 @@ static void bscan_sweep(AWeatherRadar *self, Sweep *sweep, colormap_t *colormap,
                guint8 **data, int *width, int *height)
 {
        /* Calculate max number of bins */
-       int i, max_bins = 0;
-       for (i = 0; i < sweep->h.nrays; i++)
+       int max_bins = 0;
+       for (int i = 0; i < sweep->h.nrays; i++)
                max_bins = MAX(max_bins, sweep->ray[i]->h.nbins);
 
        /* Allocate buffer using max number of bins for each ray */
        guint8 *buf = g_malloc0(sweep->h.nrays * max_bins * 4);
 
        /* Fill the data */
-       int ri, bi;
-       for (ri = 0; ri < sweep->h.nrays; ri++) {
+       for (int ri = 0; ri < sweep->h.nrays; ri++) {
                Ray *ray  = sweep->ray[ri];
-               for (bi = 0; bi < ray->h.nbins; bi++) {
+               for (int bi = 0; bi < ray->h.nbins; bi++) {
                        /* copy RGBA into buffer */
                        //guint val   = dz_f(ray->range[bi]);
                        guint8 val   = (guint8)ray->h.f(ray->range[bi]);
@@ -100,7 +105,7 @@ static void bscan_sweep(AWeatherRadar *self, Sweep *sweep, colormap_t *colormap,
                        buf[buf_i+0] = colormap->data[val][0];
                        buf[buf_i+1] = colormap->data[val][1];
                        buf[buf_i+2] = colormap->data[val][2];
-                       buf[buf_i+3] = colormap->data[val][3];
+                       buf[buf_i+3] = colormap->data[val][3]; // TESTING
                        if (val == BADVAL     || val == RFVAL      || val == APFLAG ||
                            val == NOTFOUND_H || val == NOTFOUND_V || val == NOECHO) {
                                buf[buf_i+3] = 0x00; // transparent
@@ -117,7 +122,8 @@ static void bscan_sweep(AWeatherRadar *self, Sweep *sweep, colormap_t *colormap,
 /* Load a sweep as the active texture */
 static void load_sweep(AWeatherRadar *self, Sweep *sweep)
 {
-       aweather_gui_gl_begin(self->gui);
+       GisOpenGL *opengl = aweather_gui_get_opengl(self->gui);
+       gis_opengl_begin(opengl);
        self->cur_sweep = sweep;
        int height, width;
        guint8 *data;
@@ -132,8 +138,8 @@ static void load_sweep(AWeatherRadar *self, Sweep *sweep)
        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, width, height, 0,
                        GL_RGBA, GL_UNSIGNED_BYTE, data);
        g_free(data);
-       aweather_gui_gl_redraw(self->gui);
-       aweather_gui_gl_end(self->gui);
+       gis_opengl_redraw(opengl);
+       gis_opengl_end(opengl);
 }
 
 static void load_colormap(AWeatherRadar *self, gchar *table)
@@ -185,7 +191,7 @@ static void load_radar_gui(AWeatherRadar *self, Radar *radar)
                                        g_snprintf(col_label_str, 64, "<b>%.2f°</b>", elev);
                                        col_label = gtk_label_new(col_label_str);
                                        gtk_label_set_use_markup(GTK_LABEL(col_label), TRUE);
-                                       gtk_widget_set_size_request(col_label, 40, -1);
+                                       gtk_widget_set_size_request(col_label, 70, -1);
                                        gtk_table_attach(GTK_TABLE(table), col_label,
                                                        cols-1,cols, 0,1, GTK_FILL,GTK_FILL, 0,0);
                                }
@@ -215,6 +221,37 @@ static void load_radar_gui(AWeatherRadar *self, Radar *radar)
        gtk_widget_show_all(table);
 }
 
+static void _aweather_radar_grid_set(GRIDCELL *grid, int gi, Ray *ray, int bi)
+{
+       Range range = ray->range[bi];
+
+       double angle = d2r(ray->h.azimuth);
+       double tilt  = d2r(ray->h.elev);
+
+       double lx    = sin(angle);
+       double ly    = cos(angle);
+       double lz    = sin(tilt);
+
+       double dist   = bi*ray->h.gate_size + ray->h.range_bin1;
+               
+       grid->p[gi].x = lx*dist;
+       grid->p[gi].y = ly*dist;
+       grid->p[gi].z = lz*dist;
+
+       guint8 val = (guint8)ray->h.f(ray->range[bi]);
+       if (val == BADVAL     || val == RFVAL      || val == APFLAG ||
+           val == NOTFOUND_H || val == NOTFOUND_V || val == NOECHO ||
+           val > 80)
+               val = 0;
+       grid->val[gi] = (float)val;
+       //g_debug("(%.2f,%.2f,%.2f) - (%.0f,%.0f,%.0f) = %d", 
+       //      angle, tilt, dist,
+       //      grid->p[gi].x,
+       //      grid->p[gi].y,
+       //      grid->p[gi].z,
+       //      val);
+}
+
 /* Load a radar from a decompressed file */
 static void load_radar(AWeatherRadar *self, gchar *radar_file)
 {
@@ -231,6 +268,81 @@ static void load_radar(AWeatherRadar *self, gchar *radar_file)
        }
        g_free(site);
 
+#ifdef MARCHING
+       /* Load the surface */
+       if (self->cur_triangles) {
+               g_free(self->cur_triangles);
+               self->cur_triangles = NULL;
+       }
+       self->cur_num_triangles = 0;
+       int x = 1;
+       for (guint vi = 0; vi < radar->h.nvolumes; vi++) {
+               if (radar->v[vi] == NULL) continue;
+
+               for (guint si = 0; si+1 < radar->v[vi]->h.nsweeps; si++) {
+                       Sweep *sweep0 = radar->v[vi]->sweep[si+0];
+                       Sweep *sweep1 = radar->v[vi]->sweep[si+1];
+
+                       //g_debug("_aweather_radar_expose: sweep[%3d-%3d] -- nrays = %d, %d",
+                       //      si, si+1,sweep0->h.nrays, sweep1->h.nrays);
+
+                       /* Skip super->regular resolution switch for now */
+                       if (sweep0 == NULL || sweep0->h.elev == 0 ||
+                           sweep1 == NULL || sweep1->h.elev == 0 ||
+                           sweep0->h.nrays != sweep1->h.nrays)
+                               continue;
+
+                       /* We repack the arrays so that raysX[0] is always north, etc */
+                       Ray **rays0 = g_malloc0(sizeof(Ray*)*sweep0->h.nrays);
+                       Ray **rays1 = g_malloc0(sizeof(Ray*)*sweep1->h.nrays);
+
+                       for (guint ri = 0; ri < sweep0->h.nrays; ri++)
+                               rays0[(guint)(sweep0->ray[ri]->h.azimuth * sweep0->h.nrays / 360)] =
+                                       sweep0->ray[ri];
+                       for (guint ri = 0; ri < sweep1->h.nrays; ri++)
+                               rays1[(guint)(sweep1->ray[ri]->h.azimuth * sweep1->h.nrays / 360)] =
+                                       sweep1->ray[ri];
+
+                       for (guint ri = 0; ri+x < sweep0->h.nrays; ri+=x) {
+                               //g_debug("_aweather_radar_expose: ray[%3d-%3d] -- nbins = %d, %d, %d, %d",
+                               //      ri, ri+x,
+                               //      rays0[ri  ]->h.nbins, 
+                               //      rays0[ri+1]->h.nbins, 
+                               //      rays1[ri  ]->h.nbins, 
+                               //      rays1[ri+1]->h.nbins);
+
+                               for (guint bi = 0; bi+x < rays1[ri]->h.nbins; bi+=x) {
+                                       GRIDCELL grid = {};
+                                       _aweather_radar_grid_set(&grid, 7, rays0[(ri  )%sweep0->h.nrays], bi+x);
+                                       _aweather_radar_grid_set(&grid, 6, rays0[(ri+x)%sweep0->h.nrays], bi+x);
+                                       _aweather_radar_grid_set(&grid, 5, rays0[(ri+x)%sweep0->h.nrays], bi  );
+                                       _aweather_radar_grid_set(&grid, 4, rays0[(ri  )%sweep0->h.nrays], bi  );
+                                       _aweather_radar_grid_set(&grid, 3, rays1[(ri  )%sweep0->h.nrays], bi+x);
+                                       _aweather_radar_grid_set(&grid, 2, rays1[(ri+x)%sweep0->h.nrays], bi+x);
+                                       _aweather_radar_grid_set(&grid, 1, rays1[(ri+x)%sweep0->h.nrays], bi  );
+                                       _aweather_radar_grid_set(&grid, 0, rays1[(ri  )%sweep0->h.nrays], bi  );
+                                       
+                                       TRIANGLE tris[10];
+                                       int n = march_one_cube(grid, 40, tris);
+
+                                       self->cur_triangles = g_realloc(self->cur_triangles,
+                                               (self->cur_num_triangles+n)*sizeof(TRIANGLE));
+                                       for (int i = 0; i < n; i++) {
+                                               //g_debug("triangle: ");
+                                               //g_debug("\t(%f,%f,%f)", tris[i].p[0].x, tris[i].p[0].y, tris[i].p[0].z);
+                                               //g_debug("\t(%f,%f,%f)", tris[i].p[1].x, tris[i].p[1].y, tris[i].p[1].z);
+                                               //g_debug("\t(%f,%f,%f)", tris[i].p[2].x, tris[i].p[2].y, tris[i].p[2].z);
+                                               self->cur_triangles[self->cur_num_triangles+i] = tris[i];
+                                       }
+                                       self->cur_num_triangles += n;
+                                       //g_debug(" ");
+                               }
+                       }
+               }
+               break; // Exit after first volume (reflectivity)
+       }
+#endif
+
        /* Load the first sweep by default */
        if (radar->h.nvolumes < 1 || radar->v[0]->h.nsweeps < 1) {
                g_warning("No sweeps found\n");
@@ -251,46 +363,10 @@ static void load_radar(AWeatherRadar *self, gchar *radar_file)
        load_radar_gui(self, radar);
 }
 
-static void update_times(AWeatherRadar *self, AWeatherView *view, char *site, char **last_time)
+/* TODO: These update times functions are getting ugly... */
+static void update_times_gtk(AWeatherRadar *self, GList *times)
 {
-       GList *times = NULL;
-       if (aweather_view_get_offline(view)) {
-               gchar *path = g_build_filename(g_get_user_cache_dir(), PACKAGE, "nexrd2", "raw", site, NULL);
-               GDir *dir = g_dir_open(path, 0, NULL);
-               if (dir) {
-                       const gchar *name;
-                       while ((name = g_dir_read_name(dir))) {
-                               times = g_list_prepend(times, g_strdup(name));
-                       }
-                       g_dir_close(dir);
-               }
-               g_free(path);
-       } else {
-               gchar *data;
-               gsize length;
-               GError *error = NULL;
-
-               char *list_uri = g_strdup_printf("http://mesonet.agron.iastate.edu/data/nexrd2/raw/%s/dir.list", site);
-               GFile *list = g_file_new_for_uri(list_uri);
-               g_file_load_contents(list, NULL, &data, &length, NULL, &error);
-               if (error) {
-                       g_warning("Error loading list for %s: %s", site, error->message);
-                       g_error_free(error);
-               } else {
-                       gchar **lines = g_strsplit(data, "\n", -1);
-                       for (int i = 0; lines[i] && lines[i][0]; i++) {
-                               char **parts = g_strsplit(lines[i], " ", 2);
-                               times = g_list_prepend(times, g_strdup(parts[1]));
-                               g_strfreev(parts);
-                       }
-                       g_strfreev(lines);
-                       g_free(data);
-               }
-
-               g_free(list_uri);
-               g_object_unref(list);
-       }
-
+       gchar *last_time = NULL;
        GRegex *regex = g_regex_new("^[A-Z]{4}_([0-9]{8}_[0-9]{4})$", 0, 0, NULL); // KLSX_20090622_2113
        GMatchInfo *info;
 
@@ -306,15 +382,56 @@ static void update_times(AWeatherRadar *self, AWeatherView *view, char *site, ch
                        g_message("adding time %s", (gchar*)cur->data);
                        gtk_list_store_insert(lstore, &iter, 0);
                        gtk_list_store_set(lstore, &iter, 0, time, -1);
-                       if (last_time)
-                               *last_time = time;
+                       last_time = time;
                }
        }
 
+       GisView *view = aweather_gui_get_view(self->gui);
+       gis_view_set_time(view, last_time);
+
        g_regex_unref(regex);
        g_list_foreach(times, (GFunc)g_free, NULL);
        g_list_free(times);
 }
+static void update_times_online_cb(char *path, gboolean updated, gpointer _self)
+{
+       GList *times = NULL;
+       gchar *data;
+       gsize length;
+       g_file_get_contents(path, &data, &length, NULL);
+       gchar **lines = g_strsplit(data, "\n", -1);
+       for (int i = 0; lines[i] && lines[i][0]; i++) {
+               char **parts = g_strsplit(lines[i], " ", 2);
+               times = g_list_prepend(times, g_strdup(parts[1]));
+               g_strfreev(parts);
+       }
+       g_strfreev(lines);
+       g_free(data);
+
+       update_times_gtk(_self, times);
+}
+static void update_times(AWeatherRadar *self, GisView *view, char *site)
+{
+       GisWorld *world = aweather_gui_get_world(self->gui);
+       if (gis_world_get_offline(world)) {
+               GList *times = NULL;
+               gchar *path = g_build_filename(g_get_user_cache_dir(), PACKAGE, "nexrd2", "raw", site, NULL);
+               GDir *dir = g_dir_open(path, 0, NULL);
+               if (dir) {
+                       const gchar *name;
+                       while ((name = g_dir_read_name(dir))) {
+                               times = g_list_prepend(times, g_strdup(name));
+                       }
+                       g_dir_close(dir);
+               }
+               g_free(path);
+               update_times_gtk(self, times);
+       } else {
+               gchar *path = g_strdup_printf("nexrd2/raw/%s/dir.list", site);
+               cache_file(nexrad_base, path, AWEATHER_REFRESH, NULL, update_times_online_cb, self);
+               /* update_times_gtk from update_times_online_cb */
+       }
+}
 
 /*****************
  * ASync helpers *
@@ -399,13 +516,12 @@ static void on_sweep_clicked(GtkRadioButton *button, gpointer _self)
        load_sweep   (self, g_object_get_data(G_OBJECT(button), "sweep"));
 }
 
-static void on_time_changed(AWeatherView *view, const char *time, gpointer _self)
+static void on_time_changed(GisView *view, const char *time, gpointer _self)
 {
        AWeatherRadar *self = AWEATHER_RADAR(_self);
        g_debug("AWeatherRadar: on_time_changed - setting time=%s", time);
        // format: http://mesonet.agron.iastate.edu/data/nexrd2/raw/KABR/KABR_20090510_0323
-       char *site = aweather_view_get_site(view);
-       char *base = "http://mesonet.agron.iastate.edu/data/";
+       char *site = gis_view_get_site(view);
        char *path = g_strdup_printf("nexrd2/raw/%s/%s_%s", site, site, time);
 
        /* Set up progress bar */
@@ -426,41 +542,35 @@ static void on_time_changed(AWeatherView *view, const char *time, gpointer _self
                RSL_free_radar(self->cur_radar);
        self->cur_radar = NULL;
        self->cur_sweep = NULL;
-       aweather_gui_gl_redraw(self->gui);
+       gis_opengl_redraw(aweather_gui_get_opengl(self->gui));
 
        /* Start loading the new radar */
        if (self->soup) {
                soup_session_abort(self->soup);
                self->soup = NULL;
        }
-       if (aweather_view_get_offline(view)) 
-               self->soup = cache_file(base, path, AWEATHER_ONCE,
+       if (gis_world_get_offline(aweather_gui_get_world(self->gui))) 
+               self->soup = cache_file(nexrad_base, path, AWEATHER_ONCE,
                                cache_chunk_cb, cache_done_cb, self);
        else 
-               self->soup = cache_file(base, path, AWEATHER_UPDATE,
+               self->soup = cache_file(nexrad_base, path, AWEATHER_UPDATE,
                                cache_chunk_cb, cache_done_cb, self);
        g_free(path);
 }
 
-static void on_site_changed(AWeatherView *view, char *site, gpointer _self)
+static void on_site_changed(GisView *view, char *site, gpointer _self)
 {
        AWeatherRadar *self = AWEATHER_RADAR(_self);
        g_debug("AWeatherRadar: on_site_changed - Loading wsr88d list for %s", site);
-       char *time = NULL;
-       update_times(self, view, site, &time);
-       aweather_view_set_time(view, time);
-
-       g_free(time);
+       update_times(self, view, site);
 }
 
-static void on_refresh(AWeatherView *view, gpointer _self)
+static void on_refresh(GisWorld *world, gpointer _self)
 {
        AWeatherRadar *self = AWEATHER_RADAR(_self);
-       char *site = aweather_view_get_site(view);
-       char *time = NULL;
-       update_times(self, view, site, &time);
-       aweather_view_set_time(view, time);
-       g_free(time);
+       GisView *view = aweather_gui_get_view(AWEATHER_RADAR(_self)->gui);
+       char *site = gis_view_get_site(view);
+       update_times(self, view, site);
 }
 
 /***********
@@ -470,10 +580,9 @@ AWeatherRadar *aweather_radar_new(AWeatherGui *gui)
 {
        g_debug("AWeatherRadar: new");
        AWeatherRadar *self = g_object_new(AWEATHER_TYPE_RADAR, NULL);
-       self->gui = gui;
+       self->gui  = gui;
 
        GtkWidget    *config  = aweather_gui_get_widget(gui, "tabs");
-       AWeatherView *view    = aweather_gui_get_view(gui);
 
        /* Add configuration tab */
        self->config_body = gtk_alignment_new(0, 0, 1, 1);
@@ -482,9 +591,11 @@ AWeatherRadar *aweather_radar_new(AWeatherGui *gui)
        gtk_notebook_prepend_page(GTK_NOTEBOOK(config), self->config_body, gtk_label_new("Radar"));
 
        /* Set up OpenGL Stuff */
-       g_signal_connect(view,    "site-changed", G_CALLBACK(on_site_changed), self);
-       g_signal_connect(view,    "time-changed", G_CALLBACK(on_time_changed), self);
-       g_signal_connect(view,    "refresh",      G_CALLBACK(on_refresh),      self);
+       GisView  *view  = aweather_gui_get_view(gui);
+       GisWorld *world = aweather_gui_get_world(gui);
+       g_signal_connect(view,  "site-changed", G_CALLBACK(on_site_changed), self);
+       g_signal_connect(view,  "time-changed", G_CALLBACK(on_time_changed), self);
+       g_signal_connect(world, "refresh",      G_CALLBACK(on_refresh),      self);
 
        return self;
 }
@@ -497,8 +608,39 @@ static void _aweather_radar_expose(AWeatherPlugin *_self)
                return;
        Sweep *sweep = self->cur_sweep;
 
-       /* Draw the rays */
+#ifdef MARCHING
+       /* Draw the surface */
+       glMatrixMode(GL_MODELVIEW);
+       glPushMatrix();
+       glDisable(GL_TEXTURE_2D);
+       float light_ambient[]  = {0.1f, 0.1f, 0.0f};
+       float light_diffuse[]  = {0.9f, 0.9f, 0.9f};
+       float light_position[] = {-300000.0f, 500000.0f, 400000.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_LIGHT0);
+       glEnable(GL_LIGHTING);
+       glEnable(GL_COLOR_MATERIAL);
+       glColor4f(1,1,1,0.75);
+       g_debug("ntri=%d", self->cur_num_triangles);
+       glBegin(GL_TRIANGLES);
+       for (int i = 0; i < self->cur_num_triangles; i++) {
+               TRIANGLE t = self->cur_triangles[i];
+               do_normal(t.p[0].x, t.p[0].y, t.p[0].z,
+                         t.p[1].x, t.p[1].y, t.p[1].z,
+                         t.p[2].x, t.p[2].y, t.p[2].z);
+               glVertex3f(t.p[0].x, t.p[0].y, t.p[0].z);
+               glVertex3f(t.p[1].x, t.p[1].y, t.p[1].z);
+               glVertex3f(t.p[2].x, t.p[2].y, t.p[2].z);
+       }
+       glEnd();
+       glPopMatrix();
+#endif
 
+       /* Draw the rays */
+       glDisable(GL_LIGHTING);
+       glDisable(GL_COLOR_MATERIAL);
        glMatrixMode(GL_MODELVIEW);
        glPushMatrix();
        glBindTexture(GL_TEXTURE_2D, self->cur_sweep_tex);
@@ -511,11 +653,11 @@ static void _aweather_radar_expose(AWeatherPlugin *_self)
                double angle = 0;
                if (ri < sweep->h.nrays) {
                        ray = sweep->ray[ri];
-                       angle = ((ray->h.azimuth - ((double)ray->h.beam_width/2.))*M_PI)/180.0; 
+                       angle = d2r(ray->h.azimuth - ((double)ray->h.beam_width/2.));
                } else {
                        /* Do the right side of the last sweep */
                        ray = sweep->ray[ri-1];
-                       angle = ((ray->h.azimuth + ((double)ray->h.beam_width/2.))*M_PI)/180.0; 
+                       angle = d2r(ray->h.azimuth + ((double)ray->h.beam_width/2.));
                }
 
                double lx = sin(angle);
@@ -530,8 +672,10 @@ static void _aweather_radar_expose(AWeatherPlugin *_self)
                glVertex3f(lx*near_dist, ly*near_dist, 2.0);
 
                // far  left
+               // todo: correct range-height function
+               double height = sin(d2r(ray->h.elev)) * far_dist;
                glTexCoord2f(1.0, (double)ri/sweep->h.nrays-0.01);
-               glVertex3f(lx*far_dist,  ly*far_dist,  2.0);
+               glVertex3f(lx*far_dist,  ly*far_dist, height);
        }
        //g_print("ri=%d, nr=%d, bw=%f\n", _ri, sweep->h.nrays, sweep->h.beam_width);
        glEnd();