UVES Pipeline Reference Manual  5.4.0
uves_plot.c
1 /* *
2  * This file is part of the ESO UVES Pipeline *
3  * Copyright (C) 2004,2005 European Southern Observatory *
4  * *
5  * This library is free software; you can redistribute it and/or modify *
6  * it under the terms of the GNU General Public License as published by *
7  * the Free Software Foundation; either version 2 of the License, or *
8  * (at your option) any later version. *
9  * *
10  * This program is distributed in the hope that it will be useful, *
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
13  * GNU General Public License for more details. *
14  * *
15  * You should have received a copy of the GNU General Public License *
16  * along with this program; if not, write to the Free Software *
17  * Foundation, 51 Franklin St, Fifth Floor, Boston, MA 02111-1307 USA *
18  * */
19 
20 /*
21  * $Author: amodigli $
22  * $Date: 2012-03-02 16:49:48 $
23  * $Revision: 1.38 $
24  * $Name: not supported by cvs2svn $
25  * $Log: not supported by cvs2svn $
26  * Revision 1.37 2011/12/08 14:02:30 amodigli
27  * Fix warnings with CPL6
28  *
29  * Revision 1.36 2011/10/26 10:55:52 amodigli
30  * removed irplib_plot dependency
31  *
32  * Revision 1.35 2010/09/24 09:32:07 amodigli
33  * put back QFITS dependency to fix problem spot by NRI on FIBER mode (with MIDAS calibs) data
34  *
35  * Revision 1.33 2009/12/15 14:07:58 amodigli
36  * changed IRPLIB_PLOTTER to CPL_PLOTTER
37  *
38  * Revision 1.32 2007/08/21 13:08:26 jmlarsen
39  * Removed irplib_access module, largely deprecated by CPL-4
40  *
41  * Revision 1.31 2007/06/06 08:17:33 amodigli
42  * replace tab with 4 spaces
43  *
44  * Revision 1.30 2007/05/23 13:03:19 jmlarsen
45  * Added missing include directive
46  *
47  * Revision 1.29 2007/05/22 11:31:35 jmlarsen
48  * Removed image plotting functionality
49  *
50  * Revision 1.28 2007/05/22 08:44:37 jmlarsen
51  * Don't rely on popen/pclose
52  *
53  * Revision 1.27 2007/04/24 09:41:35 jmlarsen
54  * Removed deprecated irplib_string_concatenate_all
55  *
56  * Revision 1.26 2007/01/15 08:48:41 jmlarsen
57  * Shortened lines
58  *
59  * Revision 1.25 2006/11/15 15:02:15 jmlarsen
60  * Implemented const safe workarounds for CPL functions
61  *
62  * Revision 1.23 2006/11/15 14:04:08 jmlarsen
63  * Removed non-const version of parameterlist_get_first/last/next which is
64  * already in CPL, added const-safe wrapper, unwrapper and deallocator functions
65  *
66  * Revision 1.22 2006/11/13 14:23:55 jmlarsen
67  * Removed workarounds for CPL const bugs
68  *
69  * Revision 1.21 2006/11/06 15:19:41 jmlarsen
70  * Removed unused include directives
71  *
72  * Revision 1.20 2006/09/20 12:53:57 jmlarsen
73  * Replaced stringcat functions with uves_sprintf()
74  *
75  * Revision 1.19 2006/08/23 15:09:23 jmlarsen
76  * Added uves_plot_bivectors
77  *
78  * Revision 1.18 2006/08/18 07:07:43 jmlarsen
79  * Switched order of cpl_calloc arguments
80  *
81  * Revision 1.17 2006/08/17 13:56:53 jmlarsen
82  * Reduced max line length
83  *
84  * Revision 1.16 2006/06/01 14:43:17 jmlarsen
85  * Added missing documentation
86  *
87  * Revision 1.15 2006/05/12 15:07:35 jmlarsen
88  * Implemented 3 sigma clipping to have better ranges for plots
89  *
90  * Revision 1.14 2006/04/24 09:22:53 jmlarsen
91  * Renamed shadowing variable
92  *
93  * Revision 1.13 2006/02/21 14:26:54 jmlarsen
94  * Minor changes
95  *
96  * Revision 1.12 2005/12/19 16:17:56 jmlarsen
97  * Replaced bool -> int
98  *
99  */
100 
101 #ifdef HAVE_CONFIG_H
102 # include <config.h>
103 #endif
104 
105 /*----------------------------------------------------------------------------*/
127 /*----------------------------------------------------------------------------*/
128 
129 /* If we can link to setenv but it is not declared, then declare it manually */
130 #if defined HAVE_SETENV && HAVE_SETENV
131 #if defined HAVE_DECL_SETENV && !HAVE_DECL_SETENV
132 int setenv(const char *name, const char *value, int overwrite);
133 #endif
134 #endif
135 
136 /*-----------------------------------------------------------------------------
137  Includes
138  -----------------------------------------------------------------------------*/
139 
140 #include <uves_plot.h>
141 
142 #include <uves_dump.h>
143 #include <uves_utils_wrappers.h>
144 #include <uves_error.h>
145 #include <uves_msg.h>
146 
147 #include <irplib_utils.h>
148 
149 #include <cpl.h>
150 
151 #include <stdarg.h>
152 #include <stdio.h>
153 #include <string.h>
154 #include <stdlib.h> /* setenv */
155 
156 /*-----------------------------------------------------------------------------
157  Functions prototypes
158  -----------------------------------------------------------------------------*/
159 static char *title_string(const char *title, int npoints);
160 /*-----------------------------------------------------------------------------
161  Defines
162  -----------------------------------------------------------------------------*/
163 #define MAXTITLELENGTH 10000
164 #define RECOVER_FROM_ERROR(EXTERNAL_COMMAND) do { \
165  if (cpl_error_get_code() != CPL_ERROR_NONE) \
166  { \
167  uves_msg_error("Could not send plot to " \
168  "command '%s': " \
169  "%s in '%s'", \
170  EXTERNAL_COMMAND, \
171  cpl_error_get_message(), \
172  cpl_error_get_where()); \
173  cpl_error_reset(); \
174  goto cleanup; \
175  } } while (false)
176 
177 
178 static char title[MAXTITLELENGTH];
179 static bool plotting_enabled = false; /* If caller forgets to call
180  the initializer, plotting
181  will be disabled */
182 static const char *plotter = "";
183 
186 /*-----------------------------------------------------------------------------
187  Implementation
188  -----------------------------------------------------------------------------*/
189 
190 /*----------------------------------------------------------------------------*/
202 /*----------------------------------------------------------------------------*/
203 cpl_error_code
204 uves_plot_initialize(const char *plotter_command)
205 {
206  char *test_cmd = NULL;
207  char *first_word = NULL;
208 
209  plotting_enabled = (strcmp(plotter_command, "no") != 0);
210 
211  /* Note that 'setenv' is *not* ANSI C. If it does not exist, tell user to
212  * define the environment variable him-/herself.
213  */
214 
215  if (plotting_enabled)
216  {
217  const char *env = "CPL_PLOTTER";
218 
219  /* Check if 'which x' returns non-zero.
220  x is the first word of plotting command.
221  Note: this assumes the environment understands
222  'which' and '> /dev/null'. If not,
223  plotting will be disabled.
224  */
225  first_word = uves_sprintf("%s ", plotter_command);
226 
227  assure( strtok(first_word, " ") != NULL, CPL_ERROR_ILLEGAL_OUTPUT,
228  "Error splitting string '%s'", first_word);
229 
230  test_cmd = uves_sprintf("which %s > /dev/null", first_word);
231 
232 #if defined HAVE_SETENV && HAVE_SETENV
233 
234  if (setenv(env, plotter_command, 1) != 0)
235  {
236  uves_msg_warning("Could not set environment variable '%s'. "
237  "Plotting disabled!", env);
238  plotting_enabled = false;
239  }
240  /* popen may return non-NULL even when the external command
241  is not available. This causes the recipe to crash when writing
242  to an invalid FILE pointer.
243  Therefore, check (using 'which') that the
244  command is available.
245  */
246  else if (system(test_cmd) != 0)
247  {
248  uves_msg_debug("Command '%s' returned non-zero", test_cmd);
249  uves_msg_warning("Command '%s' failed. Plotting disabled!", test_cmd);
250  plotting_enabled = false;
251  }
252  else
253  {
254  /* Setenv succeeded, remember command */
255  uves_msg_debug("setenv %s='%s' succeeded", env, plotter_command);
256  uves_msg_debug("Command '%s' returned zero", test_cmd);
257 
258  plotter = plotter_command;
259  }
260 #else
261  uves_msg_warning("setenv() is not available on this platform. You have to manually "
262  "set the environment variable '%s' to '%s'", env, plotter_command);
263 
264  plotter = plotter_command;
265 
266 #endif
267  }
268 
269  cleanup:
270  cpl_free(test_cmd);
271  cpl_free(first_word);
272 
273  return cpl_error_get_code();
274 }
275 
276 /*----------------------------------------------------------------------------*/
291 /*----------------------------------------------------------------------------*/
292 cpl_error_code
293 uves_plot_image_rows(const cpl_image *image, int first_row, int last_row, int step,
294  const char *xtitle, const char *ytitle, const char *format, ...)
295 {
296  va_list al;
297 
298  char *pre = NULL;
299  char *options = NULL;
300  const char *post = "";
301  cpl_image *thresholded = NULL;
302 
303  assure( image != NULL, CPL_ERROR_NULL_INPUT, "Null image");
304  if (xtitle == NULL) xtitle = "";
305  if (ytitle == NULL) ytitle = "";
306  assure( 1 <= first_row && first_row <= last_row &&
307  last_row <= cpl_image_get_size_y(image),
308  CPL_ERROR_ILLEGAL_INPUT,
309  "Illegal rows: %d - %d; rows in image = %" CPL_SIZE_FORMAT "",
310  first_row, last_row, cpl_image_get_size_y(image));
311 
312  assure( step >= 1, CPL_ERROR_ILLEGAL_INPUT,
313  "Illegal step size: %d", step);
314 
315  if (plotting_enabled)
316  {
317  const char *pre_format;
318  int row;
319 
320  /* Create pre string */
321  pre_format = "set grid; set xlabel '%s'; set ylabel '%s';";
322  pre = cpl_calloc(strlen(pre_format) +
323  strlen(xtitle) + strlen(ytitle) + 1,
324  sizeof(char));
325  sprintf(pre, pre_format, xtitle, ytitle);
326 
327 
328  va_start(al, format);
329  vsnprintf(title, MAXTITLELENGTH - 1, format, al);
330  va_end(al);
331  title[MAXTITLELENGTH - 1] = '\0';
332 
333  options = title_string(title, cpl_image_get_size_x(image));
334 
335  /* Threshold each row */
336  thresholded = cpl_image_duplicate(image);
337  for (row = first_row; row <= last_row; row++)
338  {
339  int nx = cpl_image_get_size_x(thresholded);
340  double median = cpl_image_get_median_window(thresholded,
341  1, first_row,
342  nx, last_row);
343  double stdev = cpl_image_get_stdev_window(thresholded,
344  1, first_row,
345  nx, last_row);
346 
347  double locut = median - 3*stdev;
348  double hicut = median + 3*stdev;
349 
350  int x, pis_rejected;
351 
352  for (x = 1; x <= nx; x++)
353  {
354  double data =
355  cpl_image_get(thresholded, x, row, &pis_rejected);
356  if (data < locut) data = locut;
357  if (data > hicut) data = hicut;
358  cpl_image_set(thresholded, x, row, data);
359  }
360  }
361 
362  cpl_plot_image_row(pre,
363  (strcmp(options, "t '%s'") == 0) ? "" : options,
364  post,
365  thresholded,
366  first_row, last_row, step);
367 
368  RECOVER_FROM_ERROR(plotter);
369  }
370 
371  cleanup:
372  uves_free_image(&thresholded);
373  cpl_free(pre);
374  cpl_free(options);
375 
376  return cpl_error_get_code();
377 }
378 
379 /*----------------------------------------------------------------------------*/
397 /*----------------------------------------------------------------------------*/
398 cpl_error_code
399 uves_plot_image_columns(const cpl_image *image, int first_column, int last_column, int step,
400  const char *xtitle, const char *ytitle, const char *format, ...)
401 {
402  va_list al;
403 
404  char *pre = NULL;
405  char *options = NULL;
406  const char *post = "";
407  cpl_image *thresholded = NULL;
408 
409  assure( image != NULL, CPL_ERROR_NULL_INPUT, "Null image");
410  if (xtitle == NULL) xtitle = "";
411  if (ytitle == NULL) ytitle = "";
412  assure( 1 <= first_column && first_column <= last_column &&
413  last_column <= cpl_image_get_size_x(image),
414  CPL_ERROR_ILLEGAL_INPUT,
415  "Illegal columns: %d - %d; columns in image = %" CPL_SIZE_FORMAT "",
416  first_column, last_column, cpl_image_get_size_x(image));
417 
418  assure( step >= 1, CPL_ERROR_ILLEGAL_INPUT,
419  "Illegal step size: %d", step);
420 
421  if (plotting_enabled)
422  {
423  const char *pre_format;
424  int col;
425 
426  /* Create pre string */
427  pre_format = "set grid; set xlabel '%s'; set ylabel '%s';";
428  pre = cpl_calloc(strlen(pre_format) +
429  strlen(xtitle) + strlen(ytitle) + 1,
430  sizeof(char));
431  sprintf(pre, pre_format, xtitle, ytitle);
432 
433  va_start(al, format);
434  vsnprintf(title, MAXTITLELENGTH - 1, format, al);
435  va_end(al);
436  title[MAXTITLELENGTH - 1] = '\0';
437 
438  options = title_string(title, cpl_image_get_size_y(image));
439 
440  /* Threshold each column */
441  thresholded = cpl_image_duplicate(image);
442  for (col = first_column; col <= last_column; col++)
443  {
444  int ny = cpl_image_get_size_x(thresholded);
445  double median = cpl_image_get_median_window(thresholded,
446  first_column, 1,
447  last_column, ny);
448  double stdev = cpl_image_get_stdev_window(thresholded,
449  first_column, 1,
450  last_column, ny);
451 
452  double locut = median - 3*stdev;
453  double hicut = median + 3*stdev;
454 
455  int y, pis_rejected;
456 
457  for (y = 1; y <= ny; y++)
458  {
459  double data = cpl_image_get(thresholded, col, y, &pis_rejected);
460  if (data < locut) data = locut;
461  if (data > hicut) data = hicut;
462  cpl_image_set(thresholded, col, y, data);
463  }
464  }
465 
466 
467  check( cpl_plot_image_col(pre,
468  (strcmp(options, "t '%s'") == 0) ? "" : options,
469  post,
470  image,
471  first_column, last_column, step),
472  "Error plotting image");
473 
474  RECOVER_FROM_ERROR(plotter);
475  }
476 
477  cleanup:
478  uves_free_image(&thresholded);
479  cpl_free(pre);
480  cpl_free(options);
481 
482  return cpl_error_get_code();
483 }
484 
485 /*----------------------------------------------------------------------------*/
499 /*----------------------------------------------------------------------------*/
500 void
501 uves_plot_bivectors(cpl_bivector **bivectors, char **titles,
502  int N, const char *xtitle,
503  const char *ytitle)
504 {
505  char *pre = NULL;
506  char **options = NULL;
507  const char *post = "";
508 
509  options = cpl_calloc(N, sizeof(char *)); /* Initialized to NULL */
510  assure_mem( options );
511 
512  if (plotting_enabled)
513  {
514  int npoints, i;
515  cpl_bivector *temp;
516  char *temps;
517 
518  /* Create options strings */
519 
520  npoints = 0;
521  for (i = 0; i < N; i++)
522  {
523  npoints += cpl_bivector_get_size(bivectors[i]);
524  }
525  for (i = 0; i < N; i++)
526  {
527  options[i] = title_string(titles[i], npoints);
528  }
529 
530 
531  {
532  double datamax = cpl_vector_get_max(cpl_bivector_get_y(bivectors[0]));
533  double datamin = cpl_vector_get_min(cpl_bivector_get_y(bivectors[0]));
534 
535  double locut = datamin - 0.2*(datamax-datamin);
536  double hicut = datamax + 0.2*(datamax-datamin);
537 
538  for (i = 0; i < N; i++)
539  {
540  int j;
541  for (j = 0; j < cpl_bivector_get_size(bivectors[i]); j++)
542  {
543  if (cpl_bivector_get_y_data(bivectors[i])[j] < locut)
544  {
545  cpl_bivector_get_y_data(bivectors[i])[j] = locut;
546  }
547  if (cpl_bivector_get_y_data(bivectors[i])[j] > hicut)
548  {
549  cpl_bivector_get_y_data(bivectors[i])[j] = hicut;
550  }
551  }
552  }
553  }
554 
555  /* Swap first/last bivectors */
556  temp = bivectors[0];
557  bivectors[0] = bivectors[N-1];
558  bivectors[N-1] = temp;
559 
560  temps = options[0];
561  options[0] = options[N-1];
562  options[N-1] = temps;
563 
564  pre = uves_sprintf(
565  "set grid; set xlabel '%s'; set ylabel '%s';", xtitle, ytitle);
566 
567  cpl_plot_bivectors(pre,
568  (const char **)options,
569  post,
570  (const cpl_bivector **)bivectors, N);
571 
572  RECOVER_FROM_ERROR(plotter);
573  }
574 
575  cleanup:
576  cpl_free(pre);
577  {
578  int i;
579  for (i = 0; i < N; i++)
580  {
581  cpl_free(options[i]);
582  }
583  }
584  cpl_free(options);
585  return;
586 }
587 
588 /*----------------------------------------------------------------------------*/
604 /*----------------------------------------------------------------------------*/
605 cpl_error_code
606 uves_plot_table(const cpl_table *table, const char *colx, const char *coly,
607  const char *format, ...)
608 {
609  va_list al;
610 
611  char *pre = NULL;
612  char *options = NULL;
613  const char *post = "";
614  cpl_table *thresholded = NULL;
615 
616  assure( table != NULL, CPL_ERROR_NULL_INPUT, "Null table");
617  assure( colx != NULL, CPL_ERROR_NULL_INPUT, "Null x column");
618  assure( coly != NULL, CPL_ERROR_NULL_INPUT, "Null y column");
619  assure( cpl_table_has_column(table, colx), CPL_ERROR_ILLEGAL_INPUT,
620  "No such column: '%s'", colx);
621  assure( cpl_table_has_column(table, coly), CPL_ERROR_ILLEGAL_INPUT,
622  "No such column: '%s'", coly);
623 
624  assure( cpl_table_get_column_type(table, colx) == CPL_TYPE_INT ||
625  cpl_table_get_column_type(table, colx) == CPL_TYPE_FLOAT ||
626  cpl_table_get_column_type(table, colx) == CPL_TYPE_DOUBLE,
627  CPL_ERROR_TYPE_MISMATCH,
628  "Column '%s' has type '%s'. Numerical type expected",
629  colx,
630  uves_tostring_cpl_type(cpl_table_get_column_type(table, colx)));
631 
632  assure( cpl_table_get_column_type(table, coly) == CPL_TYPE_INT ||
633  cpl_table_get_column_type(table, coly) == CPL_TYPE_FLOAT ||
634  cpl_table_get_column_type(table, coly) == CPL_TYPE_DOUBLE,
635  CPL_ERROR_TYPE_MISMATCH,
636  "Column '%s' has type '%s'. Numerical type expected",
637  coly,
638  uves_tostring_cpl_type(cpl_table_get_column_type(table, coly)));
639 
640  if (plotting_enabled)
641  {
642  const char *pre_format;
643 
644  /* Create options string */
645  va_start(al, format);
646  vsnprintf(title, MAXTITLELENGTH - 1, format, al);
647  va_end(al);
648  title[MAXTITLELENGTH - 1] = '\0';
649 
650  options = title_string(title, cpl_table_get_nrow(table));
651 
652  /* Create pre string */
653  pre_format = "set grid; set xlabel '%s'; set ylabel '%s';";
654  pre = cpl_calloc(strlen(pre_format) + strlen(colx) + strlen(coly) + 1,
655  sizeof(char));
656  /* It's a couple of bytes more than enough */
657  sprintf(pre, pre_format, colx, coly);
658 
659 
660  /* Threshold y-values to median +- 3 sigma before plotting */
661  {
662  double median, sigma, locut, hicut;
663  int i;
664 
665  median = cpl_table_get_column_median(table, coly);
666  sigma = cpl_table_get_column_stdev(table, coly);
667 
668  locut = median - 3*sigma;
669  hicut = median + 3*sigma;
670 
671  /* Copy the data we need, then threshold */
672  thresholded = cpl_table_new(cpl_table_get_nrow(table));
673  cpl_table_duplicate_column(thresholded, coly, table, coly);
674  cpl_table_duplicate_column(thresholded, colx, table, colx);
675 
676  for (i = 0; i < cpl_table_get_nrow(thresholded); i++)
677  {
678  double data = cpl_table_get(thresholded, coly, i, NULL); /* polymorphic */
679 
680  if (data < locut && data > hicut)
681  {
682  cpl_table_set_invalid(thresholded, coly, i);
683  }
684  }
685 
686  }
687 
688  cpl_plot_column(pre,
689  (strcmp(options, "t '%s'") == 0) ? "" : options,
690  post,
691  thresholded, colx, coly);
692 
693  RECOVER_FROM_ERROR(plotter);
694  }
695 
696  cleanup:
697  uves_free_table(&thresholded);
698  cpl_free(pre);
699  cpl_free(options);
700 
701  return cpl_error_get_code();
702 }
703 
704 
705 /*----------------------------------------------------------------------------*/
715 /*----------------------------------------------------------------------------*/
716 static char *
717 title_string(const char *plot_title, int npoints)
718 {
719  /* Option to choose plotting style
720  * depending on the number of points
721  */
722  const char *options = (npoints > 100) ?
723  "w points pointsize 1" :
724  "w linespoints pointsize 1";
725  /* If less than, say, 100 points, connect them with lines */
726 
727  size_t length = strlen("t '' ") + strlen(plot_title) + strlen(options) + 1;
728  char *result = cpl_calloc(length, sizeof(char));
729 
730  snprintf(result, length, "t '%s' %s", plot_title, options);
731 
732  return result;
733 }
734