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 #include <glib/gstdio.h>
26 #include "../compat.h"
31 /**************************
32 * Data loading functions *
33 **************************/
34 /* Convert a sweep to an 2d array of data points */
35 static void _bscan_sweep(Sweep *sweep, AWeatherColormap *colormap,
36 guint8 **data, int *width, int *height)
38 g_debug("AWeatherLevel2: _bscan_sweep - %p, %p, %p",
39 sweep, colormap, data);
40 /* Calculate max number of bins */
42 for (int i = 0; i < sweep->h.nrays; i++)
43 max_bins = MAX(max_bins, sweep->ray[i]->h.nbins);
45 /* Allocate buffer using max number of bins for each ray */
46 guint8 *buf = g_malloc0(sweep->h.nrays * max_bins * 4);
49 for (int ri = 0; ri < sweep->h.nrays; ri++) {
50 Ray *ray = sweep->ray[ri];
51 for (int bi = 0; bi < ray->h.nbins; bi++) {
52 guint buf_i = (ri*max_bins+bi)*4;
53 float value = ray->h.f(ray->range[bi]);
55 /* Check for bad values */
56 if (value == BADVAL || value == RFVAL || value == APFLAG ||
57 value == NOTFOUND_H || value == NOTFOUND_V || value == NOECHO) {
58 buf[buf_i+3] = 0x00; // transparent
62 /* Copy color to buffer */
63 guint8 *data = colormap_get(colormap, value);
64 buf[buf_i+0] = data[0];
65 buf[buf_i+1] = data[1];
66 buf[buf_i+2] = data[2];
67 buf[buf_i+3] = data[3]*0.75; // TESTING
73 *height = sweep->h.nrays;
77 /* Load a sweep into an OpenGL texture */
78 static void _load_sweep_gl(AWeatherLevel2 *level2)
80 g_debug("AWeatherLevel2: _load_sweep_gl");
83 _bscan_sweep(level2->sweep, level2->sweep_colors, &data, &width, &height);
84 gint tex_width = pow(2, ceil(log(width )/log(2)));
85 gint tex_height = pow(2, ceil(log(height)/log(2)));
86 level2->sweep_coords[0] = (double)width / tex_width;
87 level2->sweep_coords[1] = (double)height / tex_height;
89 if (!level2->sweep_tex)
90 glGenTextures(1, &level2->sweep_tex);
91 glBindTexture(GL_TEXTURE_2D, level2->sweep_tex);
92 glPixelStorei(GL_PACK_ALIGNMENT, 1);
93 glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
94 glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA8, tex_width, tex_height, 0,
95 GL_RGBA, GL_UNSIGNED_BYTE, NULL);
96 glTexSubImage2D(GL_TEXTURE_2D, 0, 0,0, width,height,
97 GL_RGBA, GL_UNSIGNED_BYTE, data);
98 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
99 glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
104 /* Decompress a radar file using wsr88dec */
105 static gboolean _decompress_radar(const gchar *file, const gchar *raw)
107 g_debug("AWeatherLevel2: _decompress_radar - \n\t%s\n\t%s", file, raw);
108 char *argv[] = {"wsr88ddec", (gchar*)file, (gchar*)raw, NULL};
110 GError *error = NULL;
112 NULL, // const gchar *working_directory
113 argv, // gchar **argv
114 NULL, // gchar **envp
115 G_SPAWN_SEARCH_PATH, // GSpawnFlags flags
116 NULL, // GSpawnChildSetupFunc child_setup
117 NULL, // gpointer user_data
118 NULL, // gchar *standard_output
119 NULL, // gchar *standard_output
120 &status, // gint *exit_status
121 &error); // GError **error
123 g_warning("AWeatherLevel2: _decompress_radar - %s", error->message);
128 gchar *msg = g_strdup_printf("wsr88ddec exited with status %d", status);
129 g_warning("AWeatherLevel2: _decompress_radar - %s", msg);
136 /* Load the radar into a Grits Volume */
137 static void _cart_to_sphere(VolCoord *out, VolCoord *in)
139 gdouble angle = in->x;
140 gdouble dist = in->y;
141 gdouble tilt = in->z;
142 gdouble lx = sin(angle);
143 gdouble ly = cos(angle);
144 gdouble lz = sin(tilt);
145 //out->x = (ly*dist)/20000;
146 //out->y = (lz*dist)/10000-0.5;
147 //out->z = (lx*dist)/20000-1.5;
153 static VolGrid *_load_grid(Volume *vol)
155 g_debug("AWeatherLevel2: _load_grid");
157 Sweep *sweep = vol->sweep[0];
158 Ray *ray = sweep->ray[0];
159 gint nsweeps = vol->h.nsweeps;
160 gint nrays = sweep->h.nrays/(1/sweep->h.beam_width)+1;
161 gint nbins = ray->h.nbins /(1000/ray->h.gate_size);
162 nbins = MIN(nbins, 150);
164 VolGrid *grid = vol_grid_new(nrays, nbins, nsweeps);
167 gint si=0, ri=0, bi=0;
168 for (si = 0; si < nsweeps; si++) {
169 sweep = vol->sweep[si];
170 rs = 1.0/sweep->h.beam_width;
171 for (ri = 0; ri < nrays; ri++) {
172 /* TODO: missing rays, pick ri based on azmith */
173 ray = sweep->ray[(ri*rs) % sweep->h.nrays];
174 bs = 1000/ray->h.gate_size;
175 for (bi = 0; bi < nbins; bi++) {
176 if (bi*bs >= ray->h.nbins)
178 val = ray->h.f(ray->range[bi*bs]);
179 if (val == BADVAL || val == RFVAL ||
180 val == APFLAG || val == NOECHO ||
181 val == NOTFOUND_H || val == NOTFOUND_V ||
184 VolPoint *point = vol_grid_get(grid, ri, bi, si);
186 point->c.x = deg2rad(ray->h.azimuth);
187 point->c.y = bi*bs*ray->h.gate_size + ray->h.range_bin1;
188 point->c.z = deg2rad(ray->h.elev);
191 for (si = 0; si < nsweeps; si++)
192 for (ri = 0; ri < nrays; ri++)
193 for (bi = 0; bi < nbins; bi++) {
194 VolPoint *point = vol_grid_get(grid, ri, bi, si);
196 point->value = nan("");
198 _cart_to_sphere(&point->c, &point->c);
204 /*********************
205 * Drawing functions *
206 *********************/
207 void aweather_level2_draw(GritsObject *_level2, GritsOpenGL *opengl)
209 AWeatherLevel2 *level2 = AWEATHER_LEVEL2(_level2);
210 if (!level2->sweep || !level2->sweep_tex)
214 Sweep *sweep = level2->sweep;
215 //glDisable(GL_ALPHA_TEST);
216 glDisable(GL_CULL_FACE);
217 glDisable(GL_LIGHTING);
218 glEnable(GL_TEXTURE_2D);
219 glEnable(GL_POLYGON_OFFSET_FILL);
220 glPolygonOffset(1.0, -2.0);
224 gdouble xscale = level2->sweep_coords[0];
225 gdouble yscale = level2->sweep_coords[1];
226 glBindTexture(GL_TEXTURE_2D, level2->sweep_tex);
227 glBegin(GL_TRIANGLE_STRIP);
228 for (int ri = 0; ri <= sweep->h.nrays; ri++) {
231 if (ri < sweep->h.nrays) {
232 ray = sweep->ray[ri];
233 angle = deg2rad(ray->h.azimuth - ((double)ray->h.beam_width/2.));
235 /* Do the right side of the last sweep */
236 ray = sweep->ray[ri-1];
237 angle = deg2rad(ray->h.azimuth + ((double)ray->h.beam_width/2.));
240 double lx = sin(angle);
241 double ly = cos(angle);
243 double near_dist = ray->h.range_bin1 - ((double)ray->h.gate_size/2.);
244 double far_dist = near_dist + (double)ray->h.nbins*ray->h.gate_size;
246 /* (find middle of bin) / scale for opengl */
248 glTexCoord2f(0.0, ((double)ri/sweep->h.nrays)*yscale);
249 glVertex3f(lx*near_dist, ly*near_dist, 2.0);
252 // todo: correct range-height function
253 double height = sin(deg2rad(ray->h.elev)) * far_dist;
254 glTexCoord2f(xscale, ((double)ri/sweep->h.nrays)*yscale);
255 glVertex3f(lx*far_dist, ly*far_dist, height);
258 //g_print("ri=%d, nr=%d, bw=%f\n", _ri, sweep->h.nrays, sweep->h.beam_width);
262 //glTexCoord2d( 0., 0.); glVertex3f(-500., 0., 0.); // bot left
263 //glTexCoord2d( 0., 1.); glVertex3f(-500., 500., 0.); // top left
264 //glTexCoord2d( 1., 1.); glVertex3f( 0., 500., 3.); // top right
265 //glTexCoord2d( 1., 0.); glVertex3f( 0., 0., 3.); // bot right
269 void aweather_level2_hide(GritsObject *_level2, gboolean hidden)
271 AWeatherLevel2 *level2 = AWEATHER_LEVEL2(_level2);
273 grits_object_hide(GRITS_OBJECT(level2->volume), hidden);
280 static gboolean _set_sweep_cb(gpointer _level2)
282 g_debug("AWeatherLevel2: _set_sweep_cb");
283 AWeatherLevel2 *level2 = _level2;
284 _load_sweep_gl(level2);
285 grits_object_queue_draw(_level2);
286 g_object_unref(level2);
289 void aweather_level2_set_sweep(AWeatherLevel2 *level2,
290 int type, float elev)
292 g_debug("AWeatherLevel2: set_sweep - %d %f", type, elev);
295 Volume *volume = RSL_get_volume(level2->radar, type);
297 level2->sweep = RSL_get_closest_sweep(volume, elev, 90);
298 if (!level2->sweep) return;
301 level2->sweep_colors = NULL;
302 for (int i = 0; level2->colormap[i].file; i++)
303 if (level2->colormap[i].type == type)
304 level2->sweep_colors = &level2->colormap[i];
305 if (!level2->sweep_colors) {
306 g_warning("AWeatherLevel2: set_sweep - missing colormap[%d]", type);
307 level2->sweep_colors = &level2->colormap[0];
311 g_object_ref(level2);
312 g_idle_add(_set_sweep_cb, level2);
315 void aweather_level2_set_iso(AWeatherLevel2 *level2, gfloat level)
317 g_debug("AWeatherLevel2: set_iso - %f", level);
319 if (!level2->volume) {
320 g_debug("AWeatherLevel2: set_iso - creating new volume");
321 Volume *rvol = RSL_get_volume(level2->radar, DZ_INDEX);
322 VolGrid *grid = _load_grid(rvol);
323 GritsVolume *vol = grits_volume_new(grid);
324 vol->proj = GRITS_VOLUME_CARTESIAN;
325 vol->disp = GRITS_VOLUME_SURFACE;
326 GRITS_OBJECT(vol)->center = GRITS_OBJECT(level2)->center;
327 grits_viewer_add(GRITS_OBJECT(level2)->viewer,
328 GRITS_OBJECT(vol), GRITS_LEVEL_WORLD+5, TRUE);
329 level2->volume = vol;
331 if (ISO_MIN < level && level < ISO_MAX) {
332 guint8 *data = colormap_get(&level2->colormap[0], level);
333 level2->volume->color[0] = data[0];
334 level2->volume->color[1] = data[1];
335 level2->volume->color[2] = data[2];
336 level2->volume->color[3] = data[3];
337 grits_volume_set_level(level2->volume, level);
338 grits_object_hide(GRITS_OBJECT(level2->volume), FALSE);
340 grits_object_hide(GRITS_OBJECT(level2->volume), TRUE);
344 AWeatherLevel2 *aweather_level2_new(Radar *radar, AWeatherColormap *colormap)
346 g_debug("AWeatherLevel2: new - %s", radar->h.radar_name);
347 RSL_sort_radar(radar);
348 AWeatherLevel2 *level2 = g_object_new(AWEATHER_TYPE_LEVEL2, NULL);
349 level2->radar = radar;
350 level2->colormap = colormap;
351 aweather_level2_set_sweep(level2, DZ_INDEX, 0);
354 Radar_header *h = &radar->h;
355 center.lat = (double)h->latd + (double)h->latm/60 + (double)h->lats/(60*60);
356 center.lon = (double)h->lond + (double)h->lonm/60 + (double)h->lons/(60*60);
357 center.elev = h->height;
358 GRITS_OBJECT(level2)->center = center;
362 AWeatherLevel2 *aweather_level2_new_from_file(const gchar *file, const gchar *site,
363 AWeatherColormap *colormap)
365 g_debug("AWeatherLevel2: new_from_file %s %s", site, file);
367 /* Decompress radar */
368 gchar *raw = g_strconcat(file, ".raw", NULL);
369 if (g_file_test(raw, G_FILE_TEST_EXISTS)) {
370 struct stat files, raws;
371 g_stat(file, &files);
373 if (files.st_mtime > raws.st_mtime)
374 if (!_decompress_radar(file, raw))
377 if (!_decompress_radar(file, raw))
381 /* Load the radar file */
382 RSL_read_these_sweeps("all", NULL);
383 g_debug("AWeatherLevel2: rsl read start");
384 Radar *radar = RSL_wsr88d_to_radar(raw, (gchar*)site);
385 g_debug("AWeatherLevel2: rsl read done");
390 return aweather_level2_new(radar, colormaps);
393 static void _on_sweep_clicked(GtkRadioButton *button, gpointer _level2)
395 AWeatherLevel2 *level2 = _level2;
396 if (gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(button))) {
397 gint type = (glong)g_object_get_data(G_OBJECT(button), "type");
398 gint elev = (glong)g_object_get_data(G_OBJECT(button), "elev");
399 aweather_level2_set_sweep(level2, type, (float)elev/100);
400 //level2->colormap = level2->sweep_colors;
404 static void _on_iso_changed(GtkRange *range, gpointer _level2)
406 AWeatherLevel2 *level2 = _level2;
407 gfloat level = gtk_range_get_value(range);
408 aweather_level2_set_iso(level2, level);
411 GtkWidget *aweather_level2_get_config(AWeatherLevel2 *level2)
413 Radar *radar = level2->radar;
414 g_debug("AWeatherLevel2: get_config - %p, %p", level2, radar);
415 /* Clear existing items */
417 guint rows = 1, cols = 1, cur_cols;
418 gchar row_label_str[64], col_label_str[64], button_str[64];
419 GtkWidget *row_label, *col_label, *button = NULL, *elev_box = NULL;
420 GtkWidget *table = gtk_table_new(rows, cols, FALSE);
423 gchar *date_str = g_strdup_printf("<b><i>%04d-%02d-%02d %02d:%02d</i></b>",
424 radar->h.year, radar->h.month, radar->h.day,
425 radar->h.hour, radar->h.minute);
426 GtkWidget *date_label = gtk_label_new(date_str);
427 gtk_label_set_use_markup(GTK_LABEL(date_label), TRUE);
428 gtk_table_attach(GTK_TABLE(table), date_label,
429 0,1, 0,1, GTK_FILL,GTK_FILL, 5,0);
433 for (guint vi = 0; vi < radar->h.nvolumes; vi++) {
434 Volume *vol = radar->v[vi];
435 if (vol == NULL) continue;
436 rows++; cols = 1; elev = 0;
439 g_snprintf(row_label_str, 64, "<b>%s:</b>", vol->h.type_str);
440 row_label = gtk_label_new(row_label_str);
441 gtk_label_set_use_markup(GTK_LABEL(row_label), TRUE);
442 gtk_misc_set_alignment(GTK_MISC(row_label), 1, 0.5);
443 gtk_table_attach(GTK_TABLE(table), row_label,
444 0,1, rows-1,rows, GTK_FILL,GTK_FILL, 5,0);
446 for (guint si = 0; si < vol->h.nsweeps; si++) {
447 Sweep *sweep = vol->sweep[si];
448 if (sweep == NULL || sweep->h.elev == 0) continue;
449 if (sweep->h.elev != elev) {
451 elev = sweep->h.elev;
454 g_object_get(table, "n-columns", &cur_cols, NULL);
455 if (cols > cur_cols) {
456 g_snprintf(col_label_str, 64, "<b>%.2f°</b>", elev);
457 col_label = gtk_label_new(col_label_str);
458 gtk_label_set_use_markup(GTK_LABEL(col_label), TRUE);
459 gtk_widget_set_size_request(col_label, 50, -1);
460 gtk_table_attach(GTK_TABLE(table), col_label,
461 cols-1,cols, 0,1, GTK_FILL,GTK_FILL, 0,0);
464 elev_box = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
465 gtk_box_set_homogeneous(GTK_BOX(elev_box), TRUE);
466 gtk_table_attach(GTK_TABLE(table), elev_box,
467 cols-1,cols, rows-1,rows, GTK_FILL,GTK_FILL, 0,0);
472 g_snprintf(button_str, 64, "%3.2f", elev);
473 button = gtk_radio_button_new_with_label_from_widget(
474 GTK_RADIO_BUTTON(button), button_str);
475 gtk_widget_set_size_request(button, -1, 26);
476 //button = gtk_radio_button_new_from_widget(GTK_RADIO_BUTTON(button));
477 //gtk_widget_set_size_request(button, -1, 22);
478 g_object_set(button, "draw-indicator", FALSE, NULL);
479 gtk_box_pack_end(GTK_BOX(elev_box), button, TRUE, TRUE, 0);
481 g_object_set_data(G_OBJECT(button), "level2", level2);
482 g_object_set_data(G_OBJECT(button), "type", (gpointer)(guintptr)vi);
483 g_object_set_data(G_OBJECT(button), "elev", (gpointer)(guintptr)(elev*100));
484 g_signal_connect(button, "clicked", G_CALLBACK(_on_sweep_clicked), level2);
488 /* Add Iso-surface volume */
489 g_object_get(table, "n-columns", &cols, NULL);
490 row_label = gtk_label_new("<b>Isosurface:</b>");
491 gtk_label_set_use_markup(GTK_LABEL(row_label), TRUE);
492 gtk_misc_set_alignment(GTK_MISC(row_label), 1, 0.5);
493 gtk_table_attach(GTK_TABLE(table), row_label,
494 0,1, rows,rows+1, GTK_FILL,GTK_FILL, 5,0);
495 GtkWidget *scale = gtk_scale_new_with_range(GTK_ORIENTATION_HORIZONTAL, ISO_MIN, ISO_MAX, 0.5);
496 gtk_widget_set_size_request(scale, -1, 26);
497 gtk_scale_set_value_pos(GTK_SCALE(scale), GTK_POS_LEFT);
498 gtk_range_set_inverted(GTK_RANGE(scale), TRUE);
499 gtk_range_set_value(GTK_RANGE(scale), ISO_MAX);
500 g_signal_connect(scale, "value-changed", G_CALLBACK(_on_iso_changed), level2);
501 gtk_table_attach(GTK_TABLE(table), scale,
502 1,cols+1, rows,rows+1, GTK_FILL|GTK_EXPAND,GTK_FILL, 0,0);
503 /* Shove all the buttons to the left, but keep the slider expanded */
504 gtk_table_attach(GTK_TABLE(table), gtk_label_new(""),
505 cols,cols+1, 0,1, GTK_FILL|GTK_EXPAND,GTK_FILL, 0,0);
512 G_DEFINE_TYPE(AWeatherLevel2, aweather_level2, GRITS_TYPE_OBJECT);
513 static void aweather_level2_init(AWeatherLevel2 *level2)
516 static void aweather_level2_dispose(GObject *_level2)
518 AWeatherLevel2 *level2 = AWEATHER_LEVEL2(_level2);
519 g_debug("AWeatherLevel2: dispose - %p", _level2);
520 if (level2->volume) {
521 grits_viewer_remove(GRITS_OBJECT(level2->volume)->viewer,
522 GRITS_OBJECT(level2->volume));
523 level2->volume = NULL;
525 G_OBJECT_CLASS(aweather_level2_parent_class)->dispose(_level2);
527 static void aweather_level2_finalize(GObject *_level2)
529 AWeatherLevel2 *level2 = AWEATHER_LEVEL2(_level2);
530 g_debug("AWeatherLevel2: finalize - %p", _level2);
531 RSL_free_radar(level2->radar);
532 if (level2->sweep_tex)
533 glDeleteTextures(1, &level2->sweep_tex);
534 G_OBJECT_CLASS(aweather_level2_parent_class)->finalize(_level2);
536 static void aweather_level2_class_init(AWeatherLevel2Class *klass)
538 G_OBJECT_CLASS(klass)->dispose = aweather_level2_dispose;
539 G_OBJECT_CLASS(klass)->finalize = aweather_level2_finalize;
540 GRITS_OBJECT_CLASS(klass)->draw = aweather_level2_draw;
541 GRITS_OBJECT_CLASS(klass)->hide = aweather_level2_hide;