2 * Copyright (C) 2009-2010 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/>.
21 #include <glib/gstdio.h>
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)
35 g_debug("AWeatherLevel2: _bscan_sweep - %p, %p, %p",
36 sweep, colormap, data);
37 /* Calculate max number of bins */
39 for (int i = 0; i < sweep->h.nrays; i++)
40 max_bins = MAX(max_bins, sweep->ray[i]->h.nbins);
42 /* Allocate buffer using max number of bins for each ray */
43 guint8 *buf = g_malloc0(sweep->h.nrays * max_bins * 4);
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
66 *height = sweep->h.nrays;
70 /* Load a sweep into an OpenGL texture */
71 static void _load_sweep_gl(AWeatherLevel2 *self)
73 g_debug("AWeatherLevel2: _load_sweep_gl");
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;
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);
97 /* Decompress a radar file using wsr88dec */
98 static gboolean _decompress_radar(const gchar *file, const gchar *raw)
100 g_debug("AWeatherLevel2: _decompress_radar - \n\t%s\n\t%s", file, raw);
101 char *argv[] = {"wsr88ddec", (gchar*)file, (gchar*)raw, NULL};
103 GError *error = NULL;
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
116 g_warning("AWeatherLevel2: _decompress_radar - %s", error->message);
121 gchar *msg = g_strdup_printf("wsr88ddec exited with status %d", status);
122 g_warning("AWeatherLevel2: _decompress_radar - %s", msg);
130 /*********************
131 * Drawing functions *
132 *********************/
133 void aweather_level2_draw(GritsObject *_self, GritsOpenGL *opengl)
135 AWeatherLevel2 *self = AWEATHER_LEVEL2(_self);
136 if (!self->sweep || !self->sweep_tex)
140 Sweep *sweep = self->sweep;
141 glDisable(GL_ALPHA_TEST);
142 glDisable(GL_CULL_FACE);
143 glDisable(GL_LIGHTING);
144 glEnable(GL_TEXTURE_2D);
145 glEnable(GL_POLYGON_OFFSET_FILL);
146 glPolygonOffset(1.0, -2.0);
150 gdouble xscale = self->sweep_coords[0];
151 gdouble yscale = self->sweep_coords[1];
152 glBindTexture(GL_TEXTURE_2D, self->sweep_tex);
153 glBegin(GL_TRIANGLE_STRIP);
154 for (int ri = 0; ri <= sweep->h.nrays; ri++) {
157 if (ri < sweep->h.nrays) {
158 ray = sweep->ray[ri];
159 angle = deg2rad(ray->h.azimuth - ((double)ray->h.beam_width/2.));
161 /* Do the right side of the last sweep */
162 ray = sweep->ray[ri-1];
163 angle = deg2rad(ray->h.azimuth + ((double)ray->h.beam_width/2.));
166 double lx = sin(angle);
167 double ly = cos(angle);
169 double near_dist = ray->h.range_bin1 - ((double)ray->h.gate_size/2.);
170 double far_dist = near_dist + (double)ray->h.nbins*ray->h.gate_size;
172 /* (find middle of bin) / scale for opengl */
174 glTexCoord2f(0.0, ((double)ri/sweep->h.nrays)*yscale);
175 glVertex3f(lx*near_dist, ly*near_dist, 2.0);
178 // todo: correct range-height function
179 double height = sin(deg2rad(ray->h.elev)) * far_dist;
180 glTexCoord2f(xscale, ((double)ri/sweep->h.nrays)*yscale);
181 glVertex3f(lx*far_dist, ly*far_dist, height);
184 //g_print("ri=%d, nr=%d, bw=%f\n", _ri, sweep->h.nrays, sweep->h.beam_width);
188 //glTexCoord2d( 0., 0.); glVertex3f(-500., 0., 0.); // bot left
189 //glTexCoord2d( 0., 1.); glVertex3f(-500., 500., 0.); // top left
190 //glTexCoord2d( 1., 1.); glVertex3f( 0., 500., 3.); // top right
191 //glTexCoord2d( 1., 0.); glVertex3f( 0., 0., 3.); // bot right
199 static gboolean _set_sweep_cb(gpointer _self)
201 g_debug("AWeatherLevel2: _set_sweep_cb");
202 AWeatherLevel2 *self = _self;
203 _load_sweep_gl(self);
204 gtk_widget_queue_draw(GTK_WIDGET(self->viewer));
205 g_object_unref(self);
208 void aweather_level2_set_sweep(AWeatherLevel2 *self,
209 int type, float elev)
211 g_debug("AWeatherLevel2: set_sweep - %d %f", type, elev);
214 Volume *volume = RSL_get_volume(self->radar, type);
216 self->sweep = RSL_get_closest_sweep(volume, elev, 90);
217 if (!self->sweep) return;
220 self->sweep_colors = NULL;
221 for (int i = 0; self->colormap[i].name; i++)
222 if (self->colormap[i].type == type)
223 self->sweep_colors = &self->colormap[i];
224 if (!self->sweep_colors) return;
228 g_idle_add(_set_sweep_cb, self);
231 AWeatherLevel2 *aweather_level2_new(GritsViewer *viewer,
232 AWeatherColormap *colormap, Radar *radar)
234 g_debug("AWeatherLevel2: new - %s", radar->h.radar_name);
235 AWeatherLevel2 *self = g_object_new(AWEATHER_TYPE_LEVEL2, NULL);
236 self->viewer = viewer;
238 self->colormap = colormap;
239 aweather_level2_set_sweep(self, DZ_INDEX, 0);
242 Radar_header *h = &radar->h;
243 center.lat = (double)h->latd + (double)h->latm/60 + (double)h->lats/(60*60);
244 center.lon = (double)h->lond + (double)h->lonm/60 + (double)h->lons/(60*60);
245 center.elev = h->height;
246 GRITS_OBJECT(self)->center = center;
250 AWeatherLevel2 *aweather_level2_new_from_file(GritsViewer *viewer,
251 AWeatherColormap *colormap,
252 const gchar *file, const gchar *site)
254 g_debug("AWeatherLevel2: new_from_file %s %s", site, file);
256 /* Decompress radar */
257 gchar *raw = g_strconcat(file, ".raw", NULL);
258 if (g_file_test(raw, G_FILE_TEST_EXISTS)) {
259 struct stat files, raws;
260 g_stat(file, &files);
262 if (files.st_mtime > raws.st_mtime)
263 if (!_decompress_radar(file, raw))
266 if (!_decompress_radar(file, raw))
270 /* Load the radar file */
271 RSL_read_these_sweeps("all", NULL);
272 g_message("read start");
273 Radar *radar = RSL_wsr88d_to_radar(raw, (gchar*)site);
274 g_message("read done");
279 return aweather_level2_new(viewer, colormaps, radar);
282 static void _on_sweep_clicked(GtkRadioButton *button, gpointer _level2)
284 AWeatherLevel2 *level2 = _level2;
285 gint type = (gint)g_object_get_data(G_OBJECT(button), "type");
286 gint elev = (gint)g_object_get_data(G_OBJECT(button), "elev");
287 aweather_level2_set_sweep(level2, type, (float)elev/100);
288 //self->colormap = level2->sweep_colors;
291 GtkWidget *aweather_level2_get_config(AWeatherLevel2 *level2)
293 Radar *radar = level2->radar;
294 g_debug("AWeatherLevel2: get_config - %p, %p", level2, radar);
295 /* Clear existing items */
297 guint rows = 1, cols = 1, cur_cols;
298 gchar row_label_str[64], col_label_str[64], button_str[64];
299 GtkWidget *row_label, *col_label, *button = NULL, *elev_box = NULL;
300 GtkWidget *table = gtk_table_new(rows, cols, FALSE);
303 gchar *date_str = g_strdup_printf("<b><i>%04d-%02d-%02d %02d:%02d</i></b>",
304 radar->h.year, radar->h.month, radar->h.day,
305 radar->h.hour, radar->h.minute);
306 GtkWidget *date_label = gtk_label_new(date_str);
307 gtk_label_set_use_markup(GTK_LABEL(date_label), TRUE);
308 gtk_table_attach(GTK_TABLE(table), date_label,
309 0,1, 0,1, GTK_FILL,GTK_FILL, 5,0);
312 for (guint vi = 0; vi < radar->h.nvolumes; vi++) {
313 Volume *vol = radar->v[vi];
314 if (vol == NULL) continue;
315 rows++; cols = 1; elev = 0;
318 g_snprintf(row_label_str, 64, "<b>%s:</b>", vol->h.type_str);
319 row_label = gtk_label_new(row_label_str);
320 gtk_label_set_use_markup(GTK_LABEL(row_label), TRUE);
321 gtk_misc_set_alignment(GTK_MISC(row_label), 1, 0.5);
322 gtk_table_attach(GTK_TABLE(table), row_label,
323 0,1, rows-1,rows, GTK_FILL,GTK_FILL, 5,0);
325 for (guint si = 0; si < vol->h.nsweeps; si++) {
326 Sweep *sweep = vol->sweep[si];
327 if (sweep == NULL || sweep->h.elev == 0) continue;
328 if (sweep->h.elev != elev) {
330 elev = sweep->h.elev;
333 g_object_get(table, "n-columns", &cur_cols, NULL);
334 if (cols > cur_cols) {
335 g_snprintf(col_label_str, 64, "<b>%.2f°</b>", elev);
336 col_label = gtk_label_new(col_label_str);
337 gtk_label_set_use_markup(GTK_LABEL(col_label), TRUE);
338 gtk_widget_set_size_request(col_label, 50, -1);
339 gtk_table_attach(GTK_TABLE(table), col_label,
340 cols-1,cols, 0,1, GTK_FILL,GTK_FILL, 0,0);
343 elev_box = gtk_hbox_new(TRUE, 0);
344 gtk_table_attach(GTK_TABLE(table), elev_box,
345 cols-1,cols, rows-1,rows, GTK_FILL,GTK_FILL, 0,0);
350 g_snprintf(button_str, 64, "%3.2f", elev);
351 button = gtk_radio_button_new_with_label_from_widget(
352 GTK_RADIO_BUTTON(button), button_str);
353 gtk_widget_set_size_request(button, -1, 26);
354 //button = gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(button));
355 //gtk_widget_set_size_request(button, -1, 22);
356 g_object_set(button, "draw-indicator", FALSE, NULL);
357 gtk_box_pack_end(GTK_BOX(elev_box), button, TRUE, TRUE, 0);
359 g_object_set_data(G_OBJECT(button), "level2", (gpointer)level2);
360 g_object_set_data(G_OBJECT(button), "type", (gpointer)vi);
361 g_object_set_data(G_OBJECT(button), "elev", (gpointer)(int)(elev*100));
362 g_signal_connect(button, "clicked", G_CALLBACK(_on_sweep_clicked), level2);
371 G_DEFINE_TYPE(AWeatherLevel2, aweather_level2, GRITS_TYPE_OBJECT);
372 static void aweather_level2_init(AWeatherLevel2 *self)
375 static void aweather_level2_finalize(GObject *_self)
377 AWeatherLevel2 *self = AWEATHER_LEVEL2(_self);
378 g_debug("AWeatherLevel2: finalize - %p", _self);
379 RSL_free_radar(self->radar);
381 glDeleteTextures(1, &self->sweep_tex);
382 G_OBJECT_CLASS(aweather_level2_parent_class)->finalize(_self);
384 static void aweather_level2_class_init(AWeatherLevel2Class *klass)
386 G_OBJECT_CLASS(klass)->finalize = aweather_level2_finalize;
387 GRITS_OBJECT_CLASS(klass)->draw = aweather_level2_draw;