]> Pileus Git - aweather/blob - src/plugins/level2.c
Use power of two textures for level2 sweeps
[aweather] / src / plugins / level2.c
1 /*
2  * Copyright (C) 2009-2010 Andy Spencer <andy753421@gmail.com>
3  *
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.
8  *
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.
13  *
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/>.
16  */
17
18 #include <config.h>
19 #include <math.h>
20 #include <GL/gl.h>
21 #include <glib/gstdio.h>
22 #include <gis.h>
23 #include <rsl.h>
24
25 #include "level2.h"
26
27
28 /**************************
29  * Data loading functions *
30  **************************/
31 /* Convert a sweep to an 2d array of data points */
32 static void _bscan_sweep(Sweep *sweep, AWeatherColormap *colormap,
33                 guint8 **data, int *width, int *height)
34 {
35         g_debug("AWeatherLevel2: _bscan_sweep - %p, %p, %p",
36                         sweep, colormap, data);
37         /* Calculate max number of bins */
38         int max_bins = 0;
39         for (int i = 0; i < sweep->h.nrays; i++)
40                 max_bins = MAX(max_bins, sweep->ray[i]->h.nbins);
41
42         /* Allocate buffer using max number of bins for each ray */
43         guint8 *buf = g_malloc0(sweep->h.nrays * max_bins * 4);
44
45         /* Fill the data */
46         for (int ri = 0; ri < sweep->h.nrays; ri++) {
47                 Ray *ray  = sweep->ray[ri];
48                 for (int bi = 0; bi < ray->h.nbins; bi++) {
49                         /* copy RGBA into buffer */
50                         //guint val   = dz_f(ray->range[bi]);
51                         guint8 val   = (guint8)ray->h.f(ray->range[bi]);
52                         guint  buf_i = (ri*max_bins+bi)*4;
53                         buf[buf_i+0] = colormap->data[val][0];
54                         buf[buf_i+1] = colormap->data[val][1];
55                         buf[buf_i+2] = colormap->data[val][2];
56                         buf[buf_i+3] = colormap->data[val][3]*0.75; // TESTING
57                         if (val == BADVAL     || val == RFVAL      || val == APFLAG ||
58                             val == NOTFOUND_H || val == NOTFOUND_V || val == NOECHO) {
59                                 buf[buf_i+3] = 0x00; // transparent
60                         }
61                 }
62         }
63
64         /* set output */
65         *width  = max_bins;
66         *height = sweep->h.nrays;
67         *data   = buf;
68 }
69
70 /* Load a sweep into an OpenGL texture */
71 static void _load_sweep_gl(AWeatherLevel2 *self)
72 {
73         g_debug("AWeatherLevel2: _load_sweep_gl");
74         guint8 *data;
75         gint width, height;
76         _bscan_sweep(self->sweep, self->sweep_colors, &data, &width, &height);
77         gint tex_width  = pow(2, ceil(log(width )/log(2)));
78         gint tex_height = pow(2, ceil(log(height)/log(2)));
79         self->sweep_coords[0] = (double)width  / tex_width;
80         self->sweep_coords[1] = (double)height / tex_height;
81
82         if (!self->sweep_tex)
83                  glGenTextures(1, &self->sweep_tex);
84         glBindTexture(GL_TEXTURE_2D, self->sweep_tex);
85         glPixelStorei(GL_PACK_ALIGNMENT, 1);
86         glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
87         glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, tex_width, tex_height, 0,
88                         GL_RGBA, GL_UNSIGNED_BYTE, NULL);
89         glTexSubImage2D(GL_TEXTURE_2D, 0, 0,0, width,height,
90                         GL_RGBA, GL_UNSIGNED_BYTE, data);
91         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
92         glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
93
94         g_free(data);
95 }
96
97 /* Decompress a radar file using wsr88dec */
98 static gboolean _decompress_radar(const gchar *file, const gchar *raw)
99 {
100         g_debug("AWeatherLevel2: _decompress_radar - \n\t%s\n\t%s", file, raw);
101         char *argv[] = {"wsr88ddec", (gchar*)file, (gchar*)raw, NULL};
102         gint status;
103         GError *error = NULL;
104         g_spawn_sync(
105                 NULL,    // const gchar *working_directory
106                 argv,    // gchar **argv
107                 NULL,    // gchar **envp
108                 G_SPAWN_SEARCH_PATH, // GSpawnFlags flags
109                 NULL,    // GSpawnChildSetupFunc child_setup
110                 NULL,    // gpointer user_data
111                 NULL,    // gchar *standard_output
112                 NULL,    // gchar *standard_output
113                 &status, // gint *exit_status
114                 &error); // GError **error
115         if (error) {
116                 g_warning("AWeatherLevel2: _decompress_radar - %s", error->message);
117                 g_error_free(error);
118                 return FALSE;
119         }
120         if (status != 0) {
121                 gchar *msg = g_strdup_printf("wsr88ddec exited with status %d", status);
122                 g_warning("AWeatherLevel2: _decompress_radar - %s", msg);
123                 g_free(msg);
124                 return FALSE;
125         }
126         return TRUE;
127 }
128
129
130 /*********************
131  * Drawing functions *
132  *********************/
133 static void _draw_radar(GisCallback *_self, gpointer _viewer)
134 {
135         AWeatherLevel2 *self = AWEATHER_LEVEL2(_self);
136         if (!self->sweep || !self->sweep_tex)
137                 return;
138
139         /* Draw wsr88d */
140         Sweep *sweep = self->sweep;
141         Radar_header *h = &self->radar->h;
142         gdouble lat  = (double)h->latd + (double)h->latm/60 + (double)h->lats/(60*60);
143         gdouble lon  = (double)h->lond + (double)h->lonm/60 + (double)h->lons/(60*60);
144         gdouble elev = h->height;
145         gis_viewer_center_position(self->viewer, lat, lon, elev);
146
147         glDisable(GL_ALPHA_TEST);
148         glDisable(GL_CULL_FACE);
149         glDisable(GL_LIGHTING);
150         glEnable(GL_TEXTURE_2D);
151         glEnable(GL_POLYGON_OFFSET_FILL);
152         glPolygonOffset(1.0, -2.0);
153         glColor4f(1,1,1,1);
154
155         /* Draw the rays */
156         gdouble xscale = self->sweep_coords[0];
157         gdouble yscale = self->sweep_coords[1];
158         glBindTexture(GL_TEXTURE_2D, self->sweep_tex);
159         glBegin(GL_TRIANGLE_STRIP);
160         for (int ri = 0; ri <= sweep->h.nrays; ri++) {
161                 Ray  *ray = NULL;
162                 double angle = 0;
163                 if (ri < sweep->h.nrays) {
164                         ray = sweep->ray[ri];
165                         angle = deg2rad(ray->h.azimuth - ((double)ray->h.beam_width/2.));
166                 } else {
167                         /* Do the right side of the last sweep */
168                         ray = sweep->ray[ri-1];
169                         angle = deg2rad(ray->h.azimuth + ((double)ray->h.beam_width/2.));
170                 }
171
172                 double lx = sin(angle);
173                 double ly = cos(angle);
174
175                 double near_dist = ray->h.range_bin1;
176                 double far_dist  = ray->h.nbins*ray->h.gate_size + ray->h.range_bin1;
177
178                 /* (find middle of bin) / scale for opengl */
179                 // near left
180                 glTexCoord2f(0.0, ((double)ri/sweep->h.nrays)*yscale);
181                 glVertex3f(lx*near_dist, ly*near_dist, 2.0);
182
183                 // far  left
184                 // todo: correct range-height function
185                 double height = sin(deg2rad(ray->h.elev)) * far_dist;
186                 glTexCoord2f(xscale, ((double)ri/sweep->h.nrays)*yscale);
187                 glVertex3f(lx*far_dist,  ly*far_dist, height);
188         }
189         glEnd();
190         //g_print("ri=%d, nr=%d, bw=%f\n", _ri, sweep->h.nrays, sweep->h.beam_width);
191
192         /* Texture debug */
193         //glBegin(GL_QUADS);
194         //glTexCoord2d( 0.,  0.); glVertex3f(-500.,   0., 0.); // bot left
195         //glTexCoord2d( 0.,  1.); glVertex3f(-500., 500., 0.); // top left
196         //glTexCoord2d( 1.,  1.); glVertex3f( 0.,   500., 3.); // top right
197         //glTexCoord2d( 1.,  0.); glVertex3f( 0.,     0., 3.); // bot right
198         //glEnd();
199 }
200
201
202 /***********
203  * Methods *
204  ***********/
205 static gboolean _set_sweep_cb(gpointer _self)
206 {
207         g_debug("AWeatherLevel2: _set_sweep_cb");
208         AWeatherLevel2 *self = _self;
209         _load_sweep_gl(self);
210         gtk_widget_queue_draw(GTK_WIDGET(self->viewer));
211         g_object_unref(self);
212         return FALSE;
213 }
214 void aweather_level2_set_sweep(AWeatherLevel2 *self,
215                 int type, float elev)
216 {
217         g_debug("AWeatherLevel2: set_sweep - %d %f", type, elev);
218
219         /* Find sweep */
220         Volume *volume = RSL_get_volume(self->radar, type);
221         if (!volume) return;
222         self->sweep = RSL_get_closest_sweep(volume, elev, 90);
223         if (!self->sweep) return;
224
225         /* Find colormap */
226         self->sweep_colors = NULL;
227         for (int i = 0; self->colormap[i].name; i++)
228                 if (self->colormap[i].type == type)
229                         self->sweep_colors = &self->colormap[i];
230         if (!self->sweep_colors) return;
231
232         /* Load data */
233         g_object_ref(self);
234         g_idle_add(_set_sweep_cb, self);
235 }
236
237 AWeatherLevel2 *aweather_level2_new(GisViewer *viewer,
238                 AWeatherColormap *colormap, Radar *radar)
239 {
240         g_debug("AWeatherLevel2: new - %s", radar->h.radar_name);
241         AWeatherLevel2 *self = g_object_new(AWEATHER_TYPE_LEVEL2, NULL);
242         self->viewer   = viewer;
243         self->radar    = radar;
244         self->colormap = colormap;
245         aweather_level2_set_sweep(self, DZ_INDEX, 0);
246         return self;
247 }
248
249 AWeatherLevel2 *aweather_level2_new_from_file(GisViewer *viewer,
250                 AWeatherColormap *colormap,
251                 const gchar *file, const gchar *site)
252 {
253         g_debug("AWeatherLevel2: new_from_file %s %s", site, file);
254
255         /* Decompress radar */
256         gchar *raw = g_strconcat(file, ".raw", NULL);
257         if (g_file_test(raw, G_FILE_TEST_EXISTS)) {
258                 struct stat files, raws;
259                 g_stat(file, &files);
260                 g_stat(raw,  &raws);
261                 if (files.st_mtime > raws.st_mtime)
262                         if (!_decompress_radar(file, raw))
263                                 return NULL;
264         } else {
265                 if (!_decompress_radar(file, raw))
266                         return NULL;
267         }
268
269         /* Load the radar file */
270         RSL_read_these_sweeps("all", NULL);
271         g_message("read start");
272         Radar *radar = RSL_wsr88d_to_radar(raw, (gchar*)site);
273         g_message("read done");
274         g_free(raw);
275         if (!radar)
276                 return NULL;
277
278         return aweather_level2_new(viewer, colormaps, radar);
279 }
280
281 static void _on_sweep_clicked(GtkRadioButton *button, gpointer _level2)
282 {
283         AWeatherLevel2 *level2 = _level2;
284         gint type = (gint)g_object_get_data(G_OBJECT(button), "type");
285         gint elev = (gint)g_object_get_data(G_OBJECT(button), "elev");
286         aweather_level2_set_sweep(level2, type, (float)elev/100);
287         //self->colormap = level2->sweep_colors;
288 }
289
290 GtkWidget *aweather_level2_get_config(AWeatherLevel2 *level2)
291 {
292         Radar *radar = level2->radar;
293         g_debug("AWeatherLevel2: get_config - %p, %p", level2, radar);
294         /* Clear existing items */
295         gdouble elev;
296         guint rows = 1, cols = 1, cur_cols;
297         gchar row_label_str[64], col_label_str[64], button_str[64];
298         GtkWidget *row_label, *col_label, *button = NULL, *elev_box = NULL;
299         GtkWidget *table = gtk_table_new(rows, cols, FALSE);
300
301         /* Add date */
302         gchar *date_str = g_strdup_printf("<b><i>%04d-%02d-%02d %02d:%02d</i></b>",
303                         radar->h.year, radar->h.month, radar->h.day,
304                         radar->h.hour, radar->h.minute);
305         GtkWidget *date_label = gtk_label_new(date_str);
306         gtk_label_set_use_markup(GTK_LABEL(date_label), TRUE);
307         gtk_table_attach(GTK_TABLE(table), date_label,
308                         0,1, 0,1, GTK_FILL,GTK_FILL, 5,0);
309         g_free(date_str);
310
311         for (guint vi = 0; vi < radar->h.nvolumes; vi++) {
312                 Volume *vol = radar->v[vi];
313                 if (vol == NULL) continue;
314                 rows++; cols = 1; elev = 0;
315
316                 /* Row label */
317                 g_snprintf(row_label_str, 64, "<b>%s:</b>", vol->h.type_str);
318                 row_label = gtk_label_new(row_label_str);
319                 gtk_label_set_use_markup(GTK_LABEL(row_label), TRUE);
320                 gtk_misc_set_alignment(GTK_MISC(row_label), 1, 0.5);
321                 gtk_table_attach(GTK_TABLE(table), row_label,
322                                 0,1, rows-1,rows, GTK_FILL,GTK_FILL, 5,0);
323
324                 for (guint si = 0; si < vol->h.nsweeps; si++) {
325                         Sweep *sweep = vol->sweep[si];
326                         if (sweep == NULL || sweep->h.elev == 0) continue;
327                         if (sweep->h.elev != elev) {
328                                 cols++;
329                                 elev = sweep->h.elev;
330
331                                 /* Column label */
332                                 g_object_get(table, "n-columns", &cur_cols, NULL);
333                                 if (cols >  cur_cols) {
334                                         g_snprintf(col_label_str, 64, "<b>%.2f°</b>", elev);
335                                         col_label = gtk_label_new(col_label_str);
336                                         gtk_label_set_use_markup(GTK_LABEL(col_label), TRUE);
337                                         gtk_widget_set_size_request(col_label, 50, -1);
338                                         gtk_table_attach(GTK_TABLE(table), col_label,
339                                                         cols-1,cols, 0,1, GTK_FILL,GTK_FILL, 0,0);
340                                 }
341
342                                 elev_box = gtk_hbox_new(TRUE, 0);
343                                 gtk_table_attach(GTK_TABLE(table), elev_box,
344                                                 cols-1,cols, rows-1,rows, GTK_FILL,GTK_FILL, 0,0);
345                         }
346
347
348                         /* Button */
349                         g_snprintf(button_str, 64, "%3.2f", elev);
350                         button = gtk_radio_button_new_with_label_from_widget(
351                                         GTK_RADIO_BUTTON(button), button_str);
352                         gtk_widget_set_size_request(button, -1, 26);
353                         //button = gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(button));
354                         //gtk_widget_set_size_request(button, -1, 22);
355                         g_object_set(button, "draw-indicator", FALSE, NULL);
356                         gtk_box_pack_end(GTK_BOX(elev_box), button, TRUE, TRUE, 0);
357
358                         g_object_set_data(G_OBJECT(button), "level2", (gpointer)level2);
359                         g_object_set_data(G_OBJECT(button), "type",   (gpointer)vi);
360                         g_object_set_data(G_OBJECT(button), "elev",   (gpointer)(int)(elev*100));
361                         g_signal_connect(button, "clicked", G_CALLBACK(_on_sweep_clicked), level2);
362                 }
363         }
364         return table;
365 }
366
367 /****************
368  * GObject code *
369  ****************/
370 G_DEFINE_TYPE(AWeatherLevel2, aweather_level2, GIS_TYPE_CALLBACK);
371 static void aweather_level2_init(AWeatherLevel2 *self)
372 {
373         GIS_CALLBACK(self)->callback  = _draw_radar;
374         GIS_CALLBACK(self)->user_data = self;
375 }
376 static void aweather_level2_dispose(GObject *_self)
377 {
378         g_debug("AWeatherLevel2: dispose - %p", _self);
379         G_OBJECT_CLASS(aweather_level2_parent_class)->dispose(_self);
380 }
381 static void aweather_level2_finalize(GObject *_self)
382 {
383         AWeatherLevel2 *self = AWEATHER_LEVEL2(_self);
384         g_debug("AWeatherLevel2: finalize - %p", _self);
385         RSL_free_radar(self->radar);
386         if (self->sweep_tex)
387                 glDeleteTextures(1, &self->sweep_tex);
388         G_OBJECT_CLASS(aweather_level2_parent_class)->finalize(_self);
389 }
390 static void aweather_level2_class_init(AWeatherLevel2Class *klass)
391 {
392         G_OBJECT_CLASS(klass)->finalize = aweather_level2_finalize;
393         G_OBJECT_CLASS(klass)->dispose  = aweather_level2_dispose;
394 }