GNSS-SDR  0.0.19
An Open Source GNSS Software Defined Receiver
gnuplot_i.h
Go to the documentation of this file.
1 /*!
2  * \file gnuplot_i.h
3  * \brief A C++ interface to gnuplot.
4  * \author Carles Fernandez-Prades, 2017. cfernandez(at)cttc.es
5  *
6  * Original source code found at https://code.google.com/archive/p/gnuplot-cpp/
7  * by Jeremy Conlin jeremit0(at)gmail.com
8  *
9  * Version history:
10  * 0. C interface
11  * by N. Devillard (27/01/03)
12  * 1. C++ interface: direct translation from the C interface
13  * by Rajarshi Guha (07/03/03)
14  * 2. corrections for Win32 compatibility
15  * by V. Chyzhdzenka (20/05/03)
16  * 3. some member functions added, corrections for Win32 and Linux
17  * compatibility
18  * by M. Burgis (10/03/08)
19  * 4. Some fixes and improvements for Linux and macOS
20  * by C. Fernandez (22/10/17)
21  *
22  * -----------------------------------------------------------------------------
23  *
24  * GNSS-SDR is a Global Navigation Satellite System software-defined receiver.
25  * This file is part of GNSS-SDR.
26  *
27  * Copyright (C) 2010-2020 (see AUTHORS file for a list of contributors)
28  * SPDX-License-Identifier: GPL-3.0-or-later
29  *
30  * -----------------------------------------------------------------------------
31  */
32 
33 
34 #ifndef GNSS_SDR_GNUPLOT_I_H
35 #define GNSS_SDR_GNUPLOT_I_H
36 
37 #include <gflags/gflags.h>
38 #include <cmath>
39 #include <cstdlib> // for getenv()
40 #include <cstring> // for strncpy
41 #include <fstream>
42 #include <iostream>
43 #include <list> // for std::list
44 #include <sstream> // for std::ostringstream
45 #include <stdexcept>
46 #include <string>
47 #include <sys/stat.h>
48 #include <vector>
49 
50 DEFINE_bool(show_plots, true, "Show plots on screen. Disable for non-interactive testing.");
51 
52 #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__)
53 // defined for 32 and 64-bit environments
54 // clang-format off
55 #include <io.h> // for _access(), _mktemp()
56 #define GP_MAX_TMP_FILES 27 // 27 temporary files it's Microsoft restriction
57 // clang-format on
58 #elif defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
59 // all UNIX-like OSs (Linux, *BSD, macOS, Solaris, ...)
60 #include <unistd.h> // for access(), mkstemp()
61 #define GP_MAX_TMP_FILES 1024
62 #else
63 #error unsupported or unknown operating system
64 #endif
65 
66 // declare classes in global namespace
67 
68 class GnuplotException : public std::runtime_error
69 {
70 public:
71  explicit GnuplotException(const std::string &msg) : std::runtime_error(msg) {}
72 };
73 
74 
75 class Gnuplot
76 {
77 private:
78  // -------------------------------------------------------------------------
79  // member data
80  //! pointer to the stream that can be used to write to the pipe
81  FILE *gnucmd;
82  //! validation of gnuplot session
83  bool valid;
84  //! true = 2d, false = 3d
85  bool two_dim;
86  //! number of plots in session
87  int nplots;
88  //! functions and data are displayed in a defined styles
89  std::string pstyle;
90  //! interpolate and approximate data in defined styles (e.g. spline)
91  std::string smooth;
92  //! list of created tmpfiles
93  std::vector<std::string> tmpfile_list;
94 
95  // -------------------------------------------------------------------------
96  // static data
97  //! number of all tmpfiles (number of tmpfiles restricted)
98  static int tmpfile_num;
99  //! name of executed GNUPlot file
100  static std::string m_sGNUPlotFileName;
101  //! gnuplot path
102  static std::string m_sGNUPlotPath;
103  //! standard terminal, used by showonscreen
104  static std::string terminal_std;
105 
106  // -------------------------------------------------------------------------
107  // member functions (auxiliary functions)
108  // ---------------------------------------------------
109  //! get_program_path(); and popen();
110  //
111  // \param --> void
112  //
113  // \return <-- void
114  // ---------------------------------------------------
115  void init();
116 
117  // ---------------------------------------------------
118  //! creates tmpfile and returns its name
119  //
120  // \param tmp --> points to the tempfile
121  //
122  // \return <-- the name of the tempfile
123  // ---------------------------------------------------
124  std::string create_tmpfile(std::ofstream &tmp);
125 
126  // ------------------------------------------------------------------------
127  //! gnuplot path found?
128  //
129  // \param ---
130  //
131  // \return <-- found the gnuplot path (yes == true, no == false)
132  // -------------------------------------------------------------------------
133  static bool get_program_path();
134 
135  // -------------------------------------------------------------------------
136  //! checks if file is available
137  //
138  // \param filename --> the filename
139  // \param mode --> the mode [optional,default value = 0]
140  //
141  // \return file exists (yes == true, no == false)
142  // -------------------------------------------------------------------------
143  bool file_available(const std::string &filename);
144 
145  // -------------------------------------------------------------------------
146  //<! \brief checks if file exists
147  //
148  // \param filename --> the filename
149  // \param mode --> the mode [optional,default value = 0]
150  //
151  // \return file exists (yes == true, no == false)
152  // -------------------------------------------------------------------------
153  static bool file_exists(const std::string &filename, int mode = 0);
154 
155 public:
156  // -------------------------------------------------------------------------
157  // \brief optional function: set Gnuplot path manual
158  // attention: for windows: path with slash '/' not backslash '\'
159  //
160  // \param path --> the gnuplot path
161  //
162  // \return true on success, false otherwise
163  // -------------------------------------------------------------------------
164  static bool set_GNUPlotPath(const std::string &path);
165 
166  // -------------------------------------------------------------------------
167  // optional: set standard terminal, used by showonscreen
168  // defaults: Windows - win, Linux - x11, Mac - aqua
169  //
170  // \param type --> the terminal type
171  //
172  // \return ---
173  // -------------------------------------------------------------------------
174  static void set_terminal_std(const std::string &type);
175 
176  // -------------------------------------------------------------------------
177  // constructors
178  // -------------------------------------------------------------------------
179 
180  //! set a style during construction
181  explicit Gnuplot(const std::string &style = "points");
182 
183  // plot a single std::vector at one go
184  Gnuplot(const std::vector<double> &x,
185  const std::string &title = "",
186  const std::string &style = "points",
187  const std::string &labelx = "x",
188  const std::string &labely = "y");
189 
190  // plot pairs std::vector at one go
191  Gnuplot(const std::vector<double> &x,
192  const std::vector<double> &y,
193  const std::string &title = "",
194  const std::string &style = "points",
195  const std::string &labelx = "x",
196  const std::string &labely = "y");
197 
198  // plot triples std::vector at one go
199  Gnuplot(const std::vector<double> &x,
200  const std::vector<double> &y,
201  const std::vector<double> &z,
202  const std::string &title = "",
203  const std::string &style = "points",
204  const std::string &labelx = "x",
205  const std::string &labely = "y",
206  const std::string &labelz = "z");
207 
208 
209  // destructor: needed to delete temporary files
210  ~Gnuplot();
211 
212  // --------------------------------------------------------------------------------
213 
214  // send a command to gnuplot
215  Gnuplot &cmd(const std::string &cmdstr);
216 
217  // ---------------------------------------------------------------------------------
218  //! Sends a command to an active gnuplot session, identical to cmd()
219  // send a command to gnuplot using the << operator
220  //
221  // \param cmdstr --> the command string
222  //
223  // \return <-- a reference to the gnuplot object
224  // ---------------------------------------------------------------------------------
225  inline Gnuplot &operator<<(const std::string &cmdstr)
226  {
227  cmd(cmdstr);
228  return (*this);
229  }
230 
231  // --------------------------------------------------------------------------------
232  // show on screen or write to file
233 
234  // sets terminal type to terminal_std
235  Gnuplot &showonscreen(); // window output is set by default (win/x11/aqua)
236 
237  // sets terminal type to unknown (disable the screen output)
238  Gnuplot &disablescreen();
239 
240  // saves a gnuplot session to a postscript file, filename without extension
241  Gnuplot &savetops(const std::string &filename = "gnuplot_output");
242 
243  // saves a gnuplot session to a pdf file, filename without extension
244  Gnuplot &savetopdf(const std::string &filename = "gnuplot_output", unsigned int font_size = 12);
245 
246  // --------------------------------------------------------------------------------
247  // set and unset
248 
249  // set line style (some of these styles require additional information):
250  // lines, points, linespoints, impulses, dots, steps, fsteps, histeps,
251  // boxes, histograms, filledcurves
252  Gnuplot &set_style(const std::string &stylestr = "points");
253 
254  // interpolation and approximation of data, arguments:
255  // csplines, bezier, acsplines (for data values > 0), sbezier, unique, frequency
256  // (works only with plot_x, plot_xy, plotfile_x, plotfile_xy
257  // (if smooth is set, set_style has no effect on data plotting)
258  Gnuplot &set_smooth(const std::string &stylestr = "csplines");
259 
260  // ----------------------------------------------------------------------
261  // \brief unset smooth
262  // attention: smooth is not set by default
263  //
264  // \param ---
265  //
266  // \return <-- a reference to a gnuplot object
267  // ----------------------------------------------------------------------
268  inline Gnuplot &unset_smooth()
269  {
270  smooth = "";
271  return *this;
272  };
273 
274  // scales the size of the points used in plots
275  Gnuplot &set_pointsize(const double pointsize = 1.0);
276 
277  // turns grid on/off
278  inline Gnuplot &set_grid()
279  {
280  cmd("set grid");
281  return *this;
282  };
283  // grid is not set by default
284  inline Gnuplot &unset_grid()
285  {
286  cmd("unset grid");
287  return *this;
288  };
289 
290  // -----------------------------------------------
291  // set the mulitplot mode
292  //
293  // \param ---
294  //
295  // \return <-- reference to the gnuplot object
296  // -----------------------------------------------
297  inline Gnuplot &set_multiplot(int rows, int cols)
298  {
299  cmd("set multiplot layout " + std::to_string(rows) + "," + std::to_string(cols)); //+ " rowfirst");
300  return *this;
301  };
302 
303  // -----------------------------------------------
304  // unsets the mulitplot mode
305  //
306  // \param ---
307  //
308  // \return <-- reference to the gnuplot object
309  // -----------------------------------------------
310  inline Gnuplot &unset_multiplot()
311  {
312  cmd("unset multiplot");
313  return *this;
314  };
315 
316  // set sampling rate of functions, or for interpolating data
317  Gnuplot &set_samples(const int samples = 100);
318  // set isoline density (grid) for plotting functions as surfaces (for 3d plots)
319  Gnuplot &set_isosamples(const int isolines = 10);
320 
321  // --------------------------------------------------------------------------
322  // enables/disables hidden line removal for surface plotting (for 3d plot)
323  //
324  // \param ---
325  //
326  // \return <-- reference to the gnuplot object
327  // --------------------------------------------------------------------------
328  Gnuplot &set_hidden3d()
329  {
330  cmd("set hidden3d");
331  return *this;
332  };
333 
334  // ---------------------------------------------------------------------------
335  // hidden3d is not set by default
336  //
337  // \param ---
338  //
339  // \return <-- reference to the gnuplot object
340  // ---------------------------------------------------------------------------
341  inline Gnuplot &unset_hidden3d()
342  {
343  cmd("unset hidden3d");
344  return *this;
345  };
346 
347  // enables/disables contour drawing for surfaces (for 3d plot)
348  // base, surface, both
349  Gnuplot &set_contour(const std::string &position = "base");
350  // --------------------------------------------------------------------------
351  // contour is not set by default, it disables contour drawing for surfaces
352  //
353  // \param ---
354  //
355  // \return <-- reference to the gnuplot object
356  // ------------------------------------------------------------------
357  inline Gnuplot &unset_contour()
358  {
359  cmd("unset contour");
360  return *this;
361  };
362 
363  // ------------------------------------------------------------
364  // enables/disables the display of surfaces (for 3d plot)
365  //
366  // \param ---
367  //
368  // \return <-- reference to the gnuplot object
369  // ------------------------------------------------------------------
370  inline Gnuplot &set_surface()
371  {
372  cmd("set surface");
373  return *this;
374  };
375 
376  // ----------------------------------------------------------
377  // surface is set by default,
378  // it disables the display of surfaces (for 3d plot)
379  //
380  // \param ---
381  //
382  // \return <-- reference to the gnuplot object
383  // ------------------------------------------------------------------
384  inline Gnuplot &unset_surface()
385  {
386  cmd("unset surface");
387  return *this;
388  }
389 
390 
391  // switches legend on/off
392  // position: inside/outside, left/center/right, top/center/bottom, nobox/box
393  Gnuplot &set_legend(const std::string &position = "default");
394 
395  // ------------------------------------------------------------------
396  // \brief Switches legend off
397  // attention:legend is set by default
398  //
399  // \param ---
400  //
401  // \return <-- reference to the gnuplot object
402  // ------------------------------------------------------------------
403  inline Gnuplot &unset_legend()
404  {
405  cmd("unset key");
406  return *this;
407  }
408 
409  // -----------------------------------------------------------------------
410  // \brief sets and clears the title of a gnuplot session
411  //
412  // \param title --> the title of the plot [optional, default == ""]
413  //
414  // \return <-- reference to the gnuplot object
415  // -----------------------------------------------------------------------
416  inline Gnuplot &set_title(const std::string &title = "")
417  {
418  std::string cmdstr;
419  cmdstr = "set title \"";
420  cmdstr += title;
421  cmdstr += "\"";
422  *this << cmdstr;
423  return *this;
424  }
425 
426  // --------------------------------------------------------------------------------
427  //! Clears the title of a gnuplot session
428  // The title is not set by default.
429  //
430  // \param ---
431  //
432  // \return <-- reference to the gnuplot object
433  // ---------------------------------------------------------------------------------
435  {
436  this->set_title();
437  return *this;
438  }
439 
440  // set x axis label
441  Gnuplot &set_ylabel(const std::string &label = "x");
442  // set y axis label
443  Gnuplot &set_xlabel(const std::string &label = "y");
444  // set z axis label
445  Gnuplot &set_zlabel(const std::string &label = "z");
446 
447  // set axis - ranges
448  Gnuplot &set_xrange(const double iFrom, const double iTo);
449  // set y-axis - ranges
450  Gnuplot &set_yrange(const double iFrom, const double iTo);
451  // set z-axis - ranges
452  Gnuplot &set_zrange(const double iFrom, const double iTo);
453 
454  // autoscale axis (set by default) of xaxis
455  //
456  // \param ---
457  //
458  // \return <-- reference to the gnuplot object
459  // -----------------------------------------------
460  inline Gnuplot &set_xautoscale()
461  {
462  cmd("set xrange restore");
463  cmd("set autoscale x");
464  return *this;
465  };
466 
467  // -----------------------------------------------
468  // autoscale axis (set by default) of yaxis
469  //
470  // \param ---
471  //
472  // \return <-- reference to the gnuplot object
473  // -----------------------------------------------
474  inline Gnuplot &set_yautoscale()
475  {
476  cmd("set yrange restore");
477  cmd("set autoscale y");
478  return *this;
479  };
480 
481  // -----------------------------------------------
482  // autoscale axis (set by default) of zaxis
483  //
484  // \param ---
485  //
486  // \return <-- reference to the gnuplot object
487  // -----------------------------------------------
488  inline Gnuplot &set_zautoscale()
489  {
490  cmd("set zrange restore");
491  cmd("set autoscale z");
492  return *this;
493  };
494 
495  // turns on/off log scaling for the specified xaxis (logscale is not set by default)
496  Gnuplot &set_xlogscale(const double base = 10);
497  // turns on/off log scaling for the specified yaxis (logscale is not set by default)
498  Gnuplot &set_ylogscale(const double base = 10);
499  // turns on/off log scaling for the specified zaxis (logscale is not set by default)
500  Gnuplot &set_zlogscale(const double base = 10);
501 
502  // -----------------------------------------------
503  // turns off log scaling for the x axis
504  //
505  // \param ---
506  //
507  // \return <-- reference to the gnuplot object
508  // -----------------------------------------------
509  inline Gnuplot &unset_xlogscale()
510  {
511  cmd("unset logscale x");
512  return *this;
513  };
514 
515  // -----------------------------------------------
516  // turns off log scaling for the y axis
517  //
518  // \param ---
519  //
520  // \return <-- reference to the gnuplot object
521  // -----------------------------------------------
522  inline Gnuplot &unset_ylogscale()
523  {
524  cmd("unset logscale y");
525  return *this;
526  };
527 
528  // -----------------------------------------------
529  // turns off log scaling for the z axis
530  //
531  // \param ---
532  //
533  // \return <-- reference to the gnuplot object
534  // -----------------------------------------------
535  inline Gnuplot &unset_zlogscale()
536  {
537  cmd("unset logscale z");
538  return *this;
539  };
540 
541  // set palette range (autoscale by default)
542  Gnuplot &set_cbrange(const double iFrom, const double iTo);
543 
544  // --------------------------------------------------------------------------------
545  // plot
546 
547  // plot a single std::vector: x
548  // from file
549  Gnuplot &plotfile_x(const std::string &filename,
550  const unsigned int column = 1,
551  const std::string &title = "");
552 
553  // from std::vector
554  template <typename X>
555  Gnuplot &plot_x(const X &x, const std::string &title = "");
556 
557  // plot x,y pairs: x y
558  // from file
559  Gnuplot &plotfile_xy(const std::string &filename,
560  const unsigned int column_x = 1,
561  const unsigned int column_y = 2,
562  const std::string &title = "",
563  const unsigned int decimate = 1);
564  // from data
565  template <typename X, typename Y>
566  Gnuplot &plot_xy(const X &x, const Y &y,
567  const std::string &title = "",
568  const unsigned int decimate = 1);
569 
570  // plot x,y pairs with dy errorbars: x y dy
571  // from file
572  Gnuplot &plotfile_xy_err(const std::string &filename,
573  const unsigned int column_x = 1,
574  const unsigned int column_y = 2,
575  const unsigned int column_dy = 3,
576  const std::string &title = "");
577  // from data
578  template <typename X, typename Y, typename E>
579  Gnuplot &plot_xy_err(const X &x, const Y &y, const E &dy,
580  const std::string &title = "");
581 
582  template <typename X, typename Y, typename E>
583  Gnuplot &plot_grid3d(const X &x, const Y &y, const E &mag,
584  const std::string &title = "");
585 
586  // plot x,y,z triples: x y z
587  // from file
588  Gnuplot &plotfile_xyz(const std::string &filename,
589  const unsigned int column_x = 1,
590  const unsigned int column_y = 2,
591  const unsigned int column_z = 3,
592  const std::string &title = "");
593  // from std::vector
594  template <typename X, typename Y, typename Z>
595  Gnuplot &plot_xyz(const X &x,
596  const Y &y,
597  const Z &z,
598  const std::string &title = "");
599 
600  // plot an equation of the form: y = ax + b, you supply a and b
601  Gnuplot &plot_slope(const double a,
602  const double b,
603  const std::string &title = "");
604 
605  // plot an equation supplied as a std::string y=f(x), write only the function f(x) not y=
606  // the independent variable has to be x
607  // binary operators: ** exponentiation, * multiply, / divide, + add, - subtract, % modulo
608  // unary operators: - minus, ! factorial
609  // elementary functions: rand(x), abs(x), sgn(x), ceil(x), floor(x), int(x), imag(x), real(x), arg(x),
610  // sqrt(x), exp(x), log(x), log10(x), sin(x), cos(x), tan(x), asin(x), acos(x), atan(x), atan2(y,x),
611  // sinh(x), cosh(x), tanh(x), asinh(x), acosh(x), atanh(x)
612  // special functions: erf(x), erfc(x), inverf(x), gamma(x), igamma(a,x), lgamma(x), ibeta(p,q,x),
613  // besj0(x), besj1(x), besy0(x), besy1(x), lambertw(x)
614  // statistical functions: norm(x), invnorm(x)
615  Gnuplot &plot_equation(const std::string &equation, const std::string &title = "");
616 
617  // plot an equation supplied as a std::string z=f(x,y), write only the function f(x,y) not z=
618  // the independent variables have to be x and y
619  Gnuplot &plot_equation3d(const std::string &equation, const std::string &title = "");
620 
621  // plot image
622  Gnuplot &plot_image(const unsigned char *ucPicBuf,
623  const unsigned int iWidth,
624  const unsigned int iHeight,
625  const std::string &title = "");
626 
627  // plot circle
628  Gnuplot &plot_circle(double east, double north, double radius, const std::string &label = "");
629 
630  // --------------------------------------------------------------------------------
631  //! replot repeats the last plot or splot command.
632  // this can be useful for viewing a plot with different set options,
633  // or when generating the same plot for several devices (showonscreen, savetops)
634  //
635  // \param ---
636  //
637  // \return ---
638  // --------------------------------------------------------------------------------
639  inline Gnuplot &replot(void)
640  {
641  if (nplots > 0)
642  {
643  cmd("replot");
644  }
645  return *this;
646  };
647 
648  // resets a gnuplot session (next plot will erase previous ones)
649  Gnuplot &reset_plot();
650 
651  // resets a gnuplot session and sets all variables to default
652  Gnuplot &reset_all();
653 
654  // deletes temporary files
655  void remove_tmpfiles();
656 
657  // -------------------------------------------------------------------
658  // \brief Is the gnuplot session valid ??
659  //
660  //
661  // \param ---
662  //
663  // \return true if valid, false if not
664  // -------------------------------------------------------------------
665  inline bool is_valid() { return (valid); };
666 };
667 
668 
669 // ----------------------------------------------------------------------------
670 //
671 // initialize static data
672 //
673 int Gnuplot::tmpfile_num = 0;
674 
675 #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__)
676 std::string Gnuplot::m_sGNUPlotFileName = "pgnuplot.exe";
677 std::string Gnuplot::m_sGNUPlotPath = "C:/program files/gnuplot/bin/";
678 #elif defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
679 std::string Gnuplot::m_sGNUPlotFileName = "gnuplot";
680 std::string Gnuplot::m_sGNUPlotPath = "/usr/local/bin/";
681 #endif
682 
683 #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__)
684 std::string Gnuplot::terminal_std = "windows";
685 #elif (defined(unix) || defined(__unix) || defined(__unix__)) && !defined(__APPLE__)
686 std::string Gnuplot::terminal_std = "x11";
687 #elif defined(__APPLE__)
688 std::string Gnuplot::terminal_std = "aqua";
689 #endif
690 
691 // ----------------------------------------------------------------------------
692 //
693 // constructor: set a style during construction
694 //
695 inline Gnuplot::Gnuplot(const std::string &style)
696  : gnucmd(nullptr), valid(false), two_dim(false), nplots(0)
697 
698 {
699  init();
700  set_style(style);
701 }
702 
703 
704 // ----------------------------------------------------------------------------
705 //
706 // constructor: open a new session, plot a signal (x)
707 //
708 inline Gnuplot::Gnuplot(const std::vector<double> &x,
709  const std::string &title,
710  const std::string &style,
711  const std::string &labelx,
712  const std::string &labely)
713  : gnucmd(nullptr), valid(false), two_dim(false), nplots(0)
714 {
715  init();
716 
717  set_style(style);
718  set_xlabel(labelx);
719  set_ylabel(labely);
720 
721  plot_x(x, title);
722 }
723 
724 
725 // ----------------------------------------------------------------------------
726 //
727 // constructor: open a new session, plot a signal (x,y)
728 //
729 inline Gnuplot::Gnuplot(const std::vector<double> &x,
730  const std::vector<double> &y,
731  const std::string &title,
732  const std::string &style,
733  const std::string &labelx,
734  const std::string &labely)
735  : gnucmd(nullptr), valid(false), two_dim(false), nplots(0)
736 {
737  init();
738 
739  set_style(style);
740  set_xlabel(labelx);
741  set_ylabel(labely);
742 
743  plot_xy(x, y, title);
744 }
745 
746 
747 // ----------------------------------------------------------------------------
748 //
749 // constructor: open a new session, plot a signal (x,y,z)
750 //
751 inline Gnuplot::Gnuplot(const std::vector<double> &x,
752  const std::vector<double> &y,
753  const std::vector<double> &z,
754  const std::string &title,
755  const std::string &style,
756  const std::string &labelx,
757  const std::string &labely,
758  const std::string &labelz)
759  : gnucmd(nullptr), valid(false), two_dim(false), nplots(0)
760 {
761  init();
762 
763  set_style(style);
764  set_xlabel(labelx);
765  set_ylabel(labely);
766  set_zlabel(labelz);
767 
768  plot_xyz(x, y, z, title);
769 }
770 
771 
772 // ----------------------------------------------------------------------------
773 //
774 // Plots a 2d graph from a list of doubles: x
775 //
776 template <typename X>
777 Gnuplot &Gnuplot::plot_x(const X &x, const std::string &title)
778 {
779  if (x.empty())
780  {
781  throw GnuplotException("std::vector too small");
782  return *this;
783  }
784 
785  std::ofstream tmp;
786  std::string name = create_tmpfile(tmp);
787  if (name.empty())
788  {
789  return *this;
790  }
791 
792  //
793  // write the data to file
794  //
795  for (unsigned int i = 0; i < x.size(); i++)
796  {
797  tmp << x[i] << '\n';
798  }
799 
800  tmp.flush();
801  tmp.close();
802 
803  plotfile_x(name, 1, title);
804 
805  return *this;
806 }
807 
808 
809 // ----------------------------------------------------------------------------
810 //
811 // Plots a 2d graph from a list of doubles: x y
812 //
813 template <typename X, typename Y>
814 Gnuplot &Gnuplot::plot_xy(const X &x, const Y &y, const std::string &title, const unsigned int decimate)
815 {
816  if (x.empty() || y.empty())
817  {
818  throw GnuplotException("std::vectors too small");
819  return *this;
820  }
821 
822  if (x.size() != y.size())
823  {
824  throw GnuplotException("Length of the std::vectors differs");
825  return *this;
826  }
827 
828  std::ofstream tmp;
829  std::string name = create_tmpfile(tmp);
830  if (name.empty())
831  {
832  return *this;
833  }
834 
835  //
836  // write the data to file
837  //
838  for (unsigned int i = 0; i < x.size(); i++)
839  {
840  tmp << x[i] << " " << y[i] << '\n';
841  }
842 
843  tmp.flush();
844  tmp.close();
845 
846  plotfile_xy(name, 1, 2, title, decimate);
847 
848  return *this;
849 }
850 
851 
852 // ---------------------------------------------------------------------------
853 //
854 // plot x,y pairs with dy errorbars
855 //
856 template <typename X, typename Y, typename E>
857 Gnuplot &Gnuplot::plot_xy_err(const X &x,
858  const Y &y,
859  const E &dy,
860  const std::string &title)
861 {
862  if (x.empty() || y.empty() || dy.empty())
863  {
864  throw GnuplotException("std::vectors too small");
865  return *this;
866  }
867 
868  if (x.size() != y.size() || y.size() != dy.size())
869  {
870  throw GnuplotException("Length of the std::vectors differs");
871  return *this;
872  }
873 
874  std::ofstream tmp;
875  std::string name = create_tmpfile(tmp);
876  if (name.empty())
877  {
878  return *this;
879  }
880 
881  //
882  // write the data to file
883  //
884  for (unsigned int i = 0; i < x.size(); i++)
885  {
886  tmp << x[i] << " " << y[i] << " " << dy[i] << '\n';
887  }
888 
889  tmp.flush();
890  tmp.close();
891 
892  // Do the actual plot
893  plotfile_xy_err(name, 1, 2, 3, title);
894 
895  return *this;
896 }
897 
898 
899 // ----------------------------------------------------------------------------
900 //
901 // Plots a 3d grid
902 //
903 template <typename X, typename Y, typename E>
904 Gnuplot &Gnuplot::plot_grid3d(const X &x,
905  const Y &y,
906  const E &mag,
907  const std::string &title)
908 {
909  if (x.empty() || y.empty())
910  {
911  throw GnuplotException("std::vectors too small");
912  return *this;
913  }
914  std::ofstream tmp;
915  std::string name = create_tmpfile(tmp);
916  if (name.empty())
917  {
918  return *this;
919  }
920 
921  //
922  // write the data to file
923  //
924  for (unsigned int i = 0; i < x.size(); i++)
925  {
926  for (unsigned int k = 0; k < y.size(); k++)
927  {
928  tmp << static_cast<float>(x.at(i)) << " " << static_cast<float>(y.at(k)) << " " << mag.at(i).at(k) << '\n';
929  }
930  tmp.flush();
931  }
932 
933  tmp.close();
934 
935  std::ostringstream cmdstr;
936  cmdstr << "set ticslevel 0\n";
937  cmdstr << "set hidden3d\n";
938  cmdstr << "unset colorbox\n";
939  cmdstr << "set border 5\n";
940  cmdstr << "unset ztics\n";
941 
942  cmdstr << " splot \"" << name << "\" u 1:2:3";
943 
944  if (title.empty())
945  {
946  cmdstr << " notitle with " << pstyle << " palette";
947  }
948  else
949  {
950  cmdstr << " title \"" << title << "\" with " << pstyle << " palette";
951  }
952  cmdstr << "\n";
953 
954  //
955  // Do the actual plot
956  //
957  cmd(cmdstr.str());
958 
959  return *this;
960 }
961 
962 // ----------------------------------------------------------------------------
963 //
964 // Plots a 3d graph from a list of doubles: x y z
965 //
966 template <typename X, typename Y, typename Z>
967 Gnuplot &Gnuplot::plot_xyz(const X &x,
968  const Y &y,
969  const Z &z,
970  const std::string &title)
971 {
972  if (x.empty() || y.empty() || z.empty())
973  {
974  throw GnuplotException("std::vectors too small");
975  return *this;
976  }
977 
978  if (x.size() != y.size() || x.size() != z.size())
979  {
980  throw GnuplotException("Length of the std::vectors differs");
981  return *this;
982  }
983 
984  std::ofstream tmp;
985  std::string name = create_tmpfile(tmp);
986  if (name.empty())
987  {
988  return *this;
989  }
990 
991  //
992  // write the data to file
993  //
994  for (unsigned int i = 0; i < x.size(); i++)
995  {
996  tmp << x[i] << " " << y[i] << " " << z[i] << '\n';
997  }
998 
999  tmp.flush();
1000  tmp.close();
1001 
1002  plotfile_xyz(name, 1, 2, 3, title);
1003 
1004  return *this;
1005 }
1006 
1007 
1008 // ----------------------------------------------------------------------------
1009 //
1010 // define static member function: set Gnuplot path manual
1011 // for windows: path with slash '/' not backslash '\'
1012 //
1013 inline bool Gnuplot::set_GNUPlotPath(const std::string &path)
1014 {
1015  std::string tmp = path + "/" + Gnuplot::m_sGNUPlotFileName;
1016 
1017 #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__)
1018  if (Gnuplot::file_exists(tmp, 0)) // check existence
1019 #elif defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
1020  if (Gnuplot::file_exists(tmp, 1)) // check existence and execution permission
1021 #endif
1022  {
1023  Gnuplot::m_sGNUPlotPath = path;
1024  return true;
1025  }
1026 
1027  Gnuplot::m_sGNUPlotPath.clear();
1028  return false;
1029 }
1030 
1031 
1032 // ----------------------------------------------------------------------------
1033 //
1034 // define static member function: set standard terminal, used by showonscreen
1035 // defaults: Windows - win, Linux - x11, Mac - aqua
1036 //
1037 inline void Gnuplot::set_terminal_std(const std::string &type)
1038 {
1039 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
1040  if (type.find("x11") != std::string::npos && std::getenv("DISPLAY") == nullptr)
1041  {
1042  throw GnuplotException("Can't find DISPLAY variable");
1043  }
1044 #endif
1045 
1046  Gnuplot::terminal_std = type;
1047  return;
1048 }
1049 
1050 
1051 // ----------------------------------------------------------------------------
1052 //
1053 // A string tokenizer taken
1054 //
1055 template <typename Container>
1056 void stringtok(Container &container,
1057  std::string const &in,
1058  const char *const delimiters = " \t\n")
1059 {
1060  const std::string::size_type len = in.length();
1061  std::string::size_type i = 0;
1062 
1063  while (i < len)
1064  {
1065  // eat leading whitespace
1066  i = in.find_first_not_of(delimiters, i);
1067 
1068  if (i == std::string::npos)
1069  {
1070  return; // nothing left but white space
1071  }
1072 
1073  // find the end of the token
1074  std::string::size_type j = in.find_first_of(delimiters, i);
1075 
1076  // push token
1077  if (j == std::string::npos)
1078  {
1079  container.push_back(in.substr(i));
1080  return;
1081  }
1082 
1083  container.push_back(in.substr(i, j - i));
1084 
1085  // set up for next loop
1086  i = j + 1;
1087  }
1088 
1089  return;
1090 }
1091 
1092 
1093 // ----------------------------------------------------------------------------
1094 //
1095 // Destructor: needed to delete temporary files
1096 //
1097 Gnuplot::~Gnuplot()
1098 {
1099 // remove_tmpfiles();
1100 // A stream opened by popen() should be closed by pclose()
1101 #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__)
1102  if (_pclose(gnucmd) == -1)
1103  {
1104 #elif defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
1105  if (pclose(gnucmd) == -1)
1106  {
1107 #endif
1108  // throw GnuplotException("Problem closing communication to gnuplot");
1109  std::cout << "Gnuplot window left open.\n";
1110  }
1111 }
1112 
1113 // ----------------------------------------------------------------------------
1114 //
1115 // Resets a gnuplot session (next plot will erase previous ones)
1116 //
1117 inline Gnuplot &Gnuplot::reset_plot()
1118 {
1119  // remove_tmpfiles();
1120  nplots = 0;
1121 
1122  return *this;
1123 }
1124 
1125 // ----------------------------------------------------------------------------
1126 //
1127 // resets a gnuplot session and sets all variables to default
1128 //
1129 inline Gnuplot &Gnuplot::reset_all()
1130 {
1131  // remove_tmpfiles();
1132  nplots = 0;
1133  cmd("reset");
1134  cmd("clear");
1135  pstyle = "points";
1136  smooth = "";
1137  showonscreen();
1138 
1139  return *this;
1140 }
1141 
1142 // ----------------------------------------------------------------------------
1143 //
1144 // Change the plotting style of a gnuplot session
1145 //
1146 inline Gnuplot &Gnuplot::set_style(const std::string &stylestr)
1147 {
1148  if (stylestr.find("lines") == std::string::npos &&
1149  stylestr.find("points") == std::string::npos &&
1150  stylestr.find("linespoints") == std::string::npos &&
1151  stylestr.find("impulses") == std::string::npos &&
1152  stylestr.find("dots") == std::string::npos &&
1153  stylestr.find("steps") == std::string::npos &&
1154  stylestr.find("fsteps") == std::string::npos &&
1155  stylestr.find("histeps") == std::string::npos &&
1156  stylestr.find("boxes") == std::string::npos && // 1-4 columns of data are required
1157  stylestr.find("filledcurves") == std::string::npos &&
1158  stylestr.find("histograms") == std::string::npos) // only for one data column
1159  // stylestr.find("labels") == std::string::npos && // 3 columns of data are required
1160  // stylestr.find("xerrorbars") == std::string::npos && // 3-4 columns of data are required
1161  // stylestr.find("xerrorlines") == std::string::npos && // 3-4 columns of data are required
1162  // stylestr.find("errorbars") == std::string::npos && // 3-4 columns of data are required
1163  // stylestr.find("errorlines") == std::string::npos && // 3-4 columns of data are required
1164  // stylestr.find("yerrorbars") == std::string::npos && // 3-4 columns of data are required
1165  // stylestr.find("yerrorlines") == std::string::npos && // 3-4 columns of data are required
1166  // stylestr.find("boxerrorbars") == std::string::npos && // 3-5 columns of data are required
1167  // stylestr.find("xyerrorbars") == std::string::npos && // 4,6,7 columns of data are required
1168  // stylestr.find("xyerrorlines") == std::string::npos && // 4,6,7 columns of data are required
1169  // stylestr.find("boxxyerrorbars") == std::string::npos && // 4,6,7 columns of data are required
1170  // stylestr.find("financebars") == std::string::npos && // 5 columns of data are required
1171  // stylestr.find("candlesticks") == std::string::npos && // 5 columns of data are required
1172  // stylestr.find("vectors") == std::string::npos &&
1173  // stylestr.find("image") == std::string::npos &&
1174  // stylestr.find("rgbimage") == std::string::npos &&
1175  // stylestr.find("pm3d") == std::string::npos )
1176  {
1177  pstyle = std::string("points");
1178  }
1179  else
1180  {
1181  pstyle = stylestr;
1182  }
1183 
1184  return *this;
1185 }
1186 
1187 // ----------------------------------------------------------------------------
1188 //
1189 // smooth: interpolation and approximation of data
1190 //
1191 inline Gnuplot &Gnuplot::set_smooth(const std::string &stylestr)
1192 {
1193  if (stylestr.find("unique") == std::string::npos &&
1194  stylestr.find("frequency") == std::string::npos &&
1195  stylestr.find("csplines") == std::string::npos &&
1196  stylestr.find("acsplines") == std::string::npos &&
1197  stylestr.find("bezier") == std::string::npos &&
1198  stylestr.find("sbezier") == std::string::npos)
1199  {
1200  smooth = "";
1201  }
1202  else
1203  {
1204  smooth = stylestr;
1205  }
1206 
1207  return *this;
1208 }
1209 
1210 
1211 // ----------------------------------------------------------------------------
1212 //
1213 // Disable screen output
1214 //
1215 inline Gnuplot &Gnuplot::disablescreen()
1216 {
1217  cmd("set output");
1218  cmd("set terminal unknown");
1219  return *this;
1220 }
1221 
1222 // ----------------------------------------------------------------------------
1223 //
1224 // sets terminal type to windows / x11
1225 //
1226 inline Gnuplot &Gnuplot::showonscreen()
1227 {
1228  std::string persist(" persist");
1229 #ifdef __APPLE__
1230  persist = "";
1231 #endif
1232  cmd("set output");
1233  cmd("set terminal " + Gnuplot::terminal_std + persist);
1234 
1235  return *this;
1236 }
1237 
1238 
1239 // ----------------------------------------------------------------------------
1240 //
1241 // saves a gnuplot session to a pdf file
1242 //
1243 inline Gnuplot &Gnuplot::savetopdf(const std::string &filename, unsigned int font_size)
1244 {
1245  std::ostringstream cmdstr;
1246  cmdstr << "set term pdfcairo enhanced color font \"Times-New-Roman," + std::to_string(font_size) + "\"\n";
1247  cmdstr << "set output \"" << filename << ".pdf\"\n";
1248  cmdstr << "replot";
1249  cmd(cmdstr.str());
1250 
1251  return *this;
1252 }
1253 
1254 
1255 // ----------------------------------------------------------------------------
1256 //
1257 // saves a gnuplot session to a postscript file
1258 //
1259 inline Gnuplot &Gnuplot::savetops(const std::string &filename)
1260 {
1261  std::ostringstream cmdstr;
1262  cmdstr << "set term postscript landscape enhanced color dashed \"Times-Roman\" 18\n";
1263  cmdstr << "set output \"" << filename << ".ps\"\n";
1264  cmdstr << "replot";
1265  cmd(cmdstr.str());
1266 
1267  return *this;
1268 }
1269 
1270 
1271 // ----------------------------------------------------------------------------
1272 //
1273 // Switches legend on
1274 //
1275 inline Gnuplot &Gnuplot::set_legend(const std::string &position)
1276 {
1277  std::ostringstream cmdstr;
1278  cmdstr << "set key " << position;
1279 
1280  cmd(cmdstr.str());
1281 
1282  return *this;
1283 }
1284 
1285 
1286 // ----------------------------------------------------------------------------
1287 //
1288 // turns on log scaling for the x axis
1289 //
1290 inline Gnuplot &Gnuplot::set_xlogscale(const double base)
1291 {
1292  std::ostringstream cmdstr;
1293 
1294  cmdstr << "set logscale x " << base;
1295  cmd(cmdstr.str());
1296 
1297  return *this;
1298 }
1299 
1300 
1301 // ----------------------------------------------------------------------------
1302 //
1303 // turns on log scaling for the y axis
1304 //
1305 inline Gnuplot &Gnuplot::set_ylogscale(const double base)
1306 {
1307  std::ostringstream cmdstr;
1308 
1309  cmdstr << "set logscale y " << base;
1310  cmd(cmdstr.str());
1311 
1312  return *this;
1313 }
1314 
1315 
1316 // ----------------------------------------------------------------------------
1317 //
1318 // turns on log scaling for the z axis
1319 //
1320 inline Gnuplot &Gnuplot::set_zlogscale(const double base)
1321 {
1322  std::ostringstream cmdstr;
1323 
1324  cmdstr << "set logscale z " << base;
1325  cmd(cmdstr.str());
1326 
1327  return *this;
1328 }
1329 
1330 
1331 // ----------------------------------------------------------------------------
1332 //
1333 // scales the size of the points used in plots
1334 //
1335 inline Gnuplot &Gnuplot::set_pointsize(const double pointsize)
1336 {
1337  std::ostringstream cmdstr;
1338  cmdstr << "set pointsize " << pointsize;
1339  cmd(cmdstr.str());
1340 
1341  return *this;
1342 }
1343 
1344 
1345 // ----------------------------------------------------------------------------
1346 //
1347 // set isoline density (grid) for plotting functions as surfaces
1348 //
1349 inline Gnuplot &Gnuplot::set_samples(const int samples)
1350 {
1351  std::ostringstream cmdstr;
1352  cmdstr << "set samples " << samples;
1353  cmd(cmdstr.str());
1354 
1355  return *this;
1356 }
1357 
1358 
1359 // ----------------------------------------------------------------------------
1360 //
1361 // set isoline density (grid) for plotting functions as surfaces
1362 //
1363 inline Gnuplot &Gnuplot::set_isosamples(const int isolines)
1364 {
1365  std::ostringstream cmdstr;
1366  cmdstr << "set isosamples " << isolines;
1367  cmd(cmdstr.str());
1368 
1369  return *this;
1370 }
1371 
1372 
1373 // ----------------------------------------------------------------------------
1374 //
1375 // enables contour drawing for surfaces set contour {base | surface | both}
1376 //
1377 inline Gnuplot &Gnuplot::set_contour(const std::string &position)
1378 {
1379  if (position.find("base") == std::string::npos &&
1380  position.find("surface") == std::string::npos &&
1381  position.find("both") == std::string::npos)
1382  {
1383  cmd("set contour base");
1384  }
1385  else
1386  {
1387  cmd("set contour " + position);
1388  }
1389 
1390  return *this;
1391 }
1392 
1393 
1394 // ----------------------------------------------------------------------------
1395 //
1396 // set labels
1397 //
1398 // set the xlabel
1399 inline Gnuplot &Gnuplot::set_xlabel(const std::string &label)
1400 {
1401  std::ostringstream cmdstr;
1402 
1403  cmdstr << "set xlabel \"" << label << "\"";
1404  cmd(cmdstr.str());
1405 
1406  return *this;
1407 }
1408 
1409 
1410 // ----------------------------------------------------------------------------
1411 // set the ylabel
1412 //
1413 inline Gnuplot &Gnuplot::set_ylabel(const std::string &label)
1414 {
1415  std::ostringstream cmdstr;
1416 
1417  cmdstr << "set ylabel \"" << label << "\"";
1418  cmd(cmdstr.str());
1419 
1420  return *this;
1421 }
1422 
1423 
1424 // ----------------------------------------------------------------------------
1425 // set the zlabel
1426 //
1427 inline Gnuplot &Gnuplot::set_zlabel(const std::string &label)
1428 {
1429  std::ostringstream cmdstr;
1430 
1431  cmdstr << "set zlabel \"" << label << "\"";
1432  cmd(cmdstr.str());
1433 
1434  return *this;
1435 }
1436 
1437 
1438 // ----------------------------------------------------------------------------
1439 //
1440 // set range
1441 //
1442 // set the xrange
1443 inline Gnuplot &Gnuplot::set_xrange(const double iFrom,
1444  const double iTo)
1445 {
1446  std::ostringstream cmdstr;
1447 
1448  cmdstr << "set xrange[" << iFrom << ":" << iTo << "]";
1449  cmd(cmdstr.str());
1450 
1451  return *this;
1452 }
1453 
1454 
1455 // ----------------------------------------------------------------------------
1456 // set the yrange
1457 //
1458 inline Gnuplot &Gnuplot::set_yrange(const double iFrom,
1459  const double iTo)
1460 {
1461  std::ostringstream cmdstr;
1462 
1463  cmdstr << "set yrange[" << iFrom << ":" << iTo << "]";
1464  cmd(cmdstr.str());
1465 
1466  return *this;
1467 }
1468 
1469 
1470 // ----------------------------------------------------------------------------
1471 // set the zrange
1472 //
1473 inline Gnuplot &Gnuplot::set_zrange(const double iFrom,
1474  const double iTo)
1475 {
1476  std::ostringstream cmdstr;
1477 
1478  cmdstr << "set zrange[" << iFrom << ":" << iTo << "]";
1479  cmd(cmdstr.str());
1480 
1481  return *this;
1482 }
1483 
1484 
1485 // ----------------------------------------------------------------------------
1486 //
1487 // set the palette range
1488 //
1489 inline Gnuplot &Gnuplot::set_cbrange(const double iFrom,
1490  const double iTo)
1491 {
1492  std::ostringstream cmdstr;
1493 
1494  cmdstr << "set cbrange[" << iFrom << ":" << iTo << "]";
1495  cmd(cmdstr.str());
1496 
1497  return *this;
1498 }
1499 
1500 
1501 // ----------------------------------------------------------------------------
1502 //
1503 // Plots a linear equation y=ax+b (where you supply the
1504 // slope a and intercept b)
1505 //
1506 inline Gnuplot &Gnuplot::plot_slope(const double a,
1507  const double b,
1508  const std::string &title)
1509 {
1510  std::ostringstream cmdstr;
1511  //
1512  // command to be sent to gnuplot
1513  //
1514  if (nplots > 0 && two_dim == true)
1515  {
1516  cmdstr << "replot ";
1517  }
1518  else
1519  {
1520  cmdstr << "plot ";
1521  }
1522 
1523  cmdstr << a << " * x + " << b << " title \"";
1524 
1525  if (title.empty())
1526  {
1527  cmdstr << "f(x) = " << a << " * x + " << b;
1528  }
1529  else
1530  {
1531  cmdstr << title;
1532  }
1533 
1534  cmdstr << "\" with " << pstyle;
1535 
1536  //
1537  // Do the actual plot
1538  //
1539  cmd(cmdstr.str());
1540 
1541  return *this;
1542 }
1543 
1544 
1545 // ----------------------------------------------------------------------------
1546 //
1547 // Plot an equation supplied as a std::string y=f(x) (only f(x) expected)
1548 //
1549 inline Gnuplot &Gnuplot::plot_equation(const std::string &equation,
1550  const std::string &title)
1551 {
1552  std::ostringstream cmdstr;
1553  //
1554  // command to be sent to gnuplot
1555  //
1556  if (nplots > 0 && two_dim == true)
1557  {
1558  cmdstr << "replot ";
1559  }
1560  else
1561  {
1562  cmdstr << "plot ";
1563  }
1564 
1565  cmdstr << equation << " title \"";
1566 
1567  if (title.empty())
1568  {
1569  cmdstr << "f(x) = " << equation;
1570  }
1571  else
1572  {
1573  cmdstr << title;
1574  }
1575 
1576  cmdstr << "\" with " << pstyle;
1577 
1578  //
1579  // Do the actual plot
1580  //
1581  cmd(cmdstr.str());
1582 
1583  return *this;
1584 }
1585 
1586 
1587 // ----------------------------------------------------------------------------
1588 //
1589 // plot an equation supplied as a std::string y=(x)
1590 //
1591 inline Gnuplot &Gnuplot::plot_equation3d(const std::string &equation,
1592  const std::string &title)
1593 {
1594  std::ostringstream cmdstr;
1595  //
1596  // command to be sent to gnuplot
1597  //
1598  if (nplots > 0 && two_dim == false)
1599  {
1600  cmdstr << "replot ";
1601  }
1602  else
1603  {
1604  cmdstr << "splot ";
1605  }
1606 
1607  cmdstr << equation << " title \"";
1608 
1609  if (title.empty())
1610  {
1611  cmdstr << "f(x,y) = " << equation;
1612  }
1613  else
1614  {
1615  cmdstr << title;
1616  }
1617 
1618  cmdstr << "\" with " << pstyle;
1619 
1620  //
1621  // Do the actual plot
1622  //
1623  cmd(cmdstr.str());
1624 
1625  return *this;
1626 }
1627 
1628 
1629 // ----------------------------------------------------------------------------
1630 //
1631 // Plots a 2d graph from a list of doubles (x) saved in a file
1632 //
1633 inline Gnuplot &Gnuplot::plotfile_x(const std::string &filename,
1634  const unsigned int column,
1635  const std::string &title)
1636 {
1637  //
1638  // check if file exists
1639  //
1640  file_available(filename);
1641 
1642  std::ostringstream cmdstr;
1643  //
1644  // command to be sent to gnuplot
1645  //
1646  if (nplots > 0 && two_dim == true)
1647  {
1648  cmdstr << "replot ";
1649  }
1650  else
1651  {
1652  cmdstr << "plot ";
1653  }
1654 
1655  cmdstr << "\"" << filename << "\" using " << column;
1656 
1657  if (title.empty())
1658  {
1659  cmdstr << " notitle ";
1660  }
1661  else
1662  {
1663  cmdstr << " title \"" << title << "\" ";
1664  }
1665 
1666  if (smooth.empty())
1667  {
1668  cmdstr << "with " << pstyle;
1669  }
1670  else
1671  {
1672  cmdstr << "smooth " << smooth;
1673  }
1674 
1675  //
1676  // Do the actual plot
1677  //
1678  cmd(cmdstr.str()); // nplots++; two_dim = true; already in cmd();
1679 
1680  return *this;
1681 }
1682 
1683 
1684 // ----------------------------------------------------------------------------
1685 //
1686 // Plots a 2d graph from a list of doubles (x y) saved in a file
1687 //
1688 inline Gnuplot &Gnuplot::plotfile_xy(const std::string &filename,
1689  const unsigned int column_x,
1690  const unsigned int column_y,
1691  const std::string &title,
1692  const unsigned int decimate)
1693 {
1694  //
1695  // check if file exists
1696  //
1697  file_available(filename);
1698 
1699  std::ostringstream cmdstr;
1700  //
1701  // command to be sent to gnuplot
1702  //
1703  if (nplots > 0 && two_dim == true)
1704  {
1705  cmdstr << "replot ";
1706  }
1707  else
1708  {
1709  cmdstr << "plot ";
1710  }
1711 
1712  cmdstr << "\"" << filename << "\" using " << column_x << ":" << column_y << " every " << std::to_string(decimate);
1713 
1714  if (title.empty())
1715  {
1716  cmdstr << " notitle ";
1717  }
1718  else
1719  {
1720  cmdstr << " title \"" << title << "\" ";
1721  }
1722 
1723  if (smooth.empty())
1724  {
1725  cmdstr << "with " << pstyle;
1726  }
1727  else
1728  {
1729  cmdstr << "smooth " << smooth;
1730  }
1731 
1732  //
1733  // Do the actual plot
1734  //
1735  cmd(cmdstr.str());
1736 
1737  return *this;
1738 }
1739 
1740 
1741 // ----------------------------------------------------------------------------
1742 //
1743 // Plots a 2d graph with errorbars from a list of doubles (x y dy) in a file
1744 //
1745 inline Gnuplot &Gnuplot::plotfile_xy_err(const std::string &filename,
1746  const unsigned int column_x,
1747  const unsigned int column_y,
1748  const unsigned int column_dy,
1749  const std::string &title)
1750 {
1751  //
1752  // check if file exists
1753  //
1754  file_available(filename);
1755 
1756  std::ostringstream cmdstr;
1757  //
1758  // command to be sent to gnuplot
1759  //
1760  if (nplots > 0 && two_dim == true)
1761  {
1762  cmdstr << "replot ";
1763  }
1764  else
1765  {
1766  cmdstr << "plot ";
1767  }
1768 
1769  cmdstr << "\"" << filename << "\" using "
1770  << column_x << ":" << column_y << ":" << column_dy
1771  << " with errorbars ";
1772 
1773  if (title.empty())
1774  {
1775  cmdstr << " notitle ";
1776  }
1777  else
1778  {
1779  cmdstr << " title \"" << title << "\" ";
1780  }
1781 
1782  //
1783  // Do the actual plot
1784  //
1785  cmd(cmdstr.str());
1786 
1787  return *this;
1788 }
1789 
1790 
1791 // ----------------------------------------------------------------------------
1792 //
1793 // Plots a 3d graph from a list of doubles (x y z) saved in a file
1794 //
1795 inline Gnuplot &Gnuplot::plotfile_xyz(const std::string &filename,
1796  const unsigned int column_x,
1797  const unsigned int column_y,
1798  const unsigned int column_z,
1799  const std::string &title)
1800 {
1801  //
1802  // check if file exists
1803  //
1804  file_available(filename);
1805 
1806  std::ostringstream cmdstr;
1807  //
1808  // command to be sent to gnuplot
1809  //
1810  if (nplots > 0 && two_dim == false)
1811  {
1812  cmdstr << "replot ";
1813  }
1814  else
1815  {
1816  cmdstr << "splot ";
1817  }
1818 
1819  cmdstr << "\"" << filename << "\" using " << column_x << ":" << column_y
1820  << ":" << column_z;
1821 
1822  if (title.empty())
1823  {
1824  cmdstr << " notitle with " << pstyle;
1825  }
1826  else
1827  {
1828  cmdstr << " title \"" << title << "\" with " << pstyle;
1829  }
1830 
1831  //
1832  // Do the actual plot
1833  //
1834  cmd(cmdstr.str());
1835 
1836  return *this;
1837 }
1838 
1839 
1840 // ----------------------------------------------------------------------------
1841 //
1842 // * note that this function is not valid for versions of GNUPlot below 4.2
1843 //
1844 inline Gnuplot &Gnuplot::plot_image(const unsigned char *ucPicBuf,
1845  const unsigned int iWidth,
1846  const unsigned int iHeight,
1847  const std::string &title)
1848 {
1849  std::ofstream tmp;
1850  std::string name = create_tmpfile(tmp);
1851  if (name.empty())
1852  {
1853  return *this;
1854  }
1855 
1856  //
1857  // write the data to file
1858  //
1859  int iIndex = 0;
1860  for (unsigned int iRow = 0; iRow < iHeight; iRow++)
1861  {
1862  for (unsigned int iColumn = 0; iColumn < iWidth; iColumn++)
1863  {
1864  tmp << iColumn << " " << iRow << " "
1865  << static_cast<float>(ucPicBuf[iIndex++]) << '\n';
1866  }
1867  }
1868 
1869  tmp.flush();
1870  tmp.close();
1871 
1872  std::ostringstream cmdstr;
1873  //
1874  // command to be sent to gnuplot
1875  //
1876  if (nplots > 0 && two_dim == true)
1877  {
1878  cmdstr << "replot ";
1879  }
1880  else
1881  {
1882  cmdstr << "plot ";
1883  }
1884 
1885  if (title.empty())
1886  {
1887  cmdstr << "\"" << name << "\" with image";
1888  }
1889  else
1890  {
1891  cmdstr << "\"" << name << "\" title \"" << title << "\" with image";
1892  }
1893 
1894  //
1895  // Do the actual plot
1896  //
1897  cmd(cmdstr.str());
1898 
1899  return *this;
1900 }
1901 
1902 
1903 inline Gnuplot &Gnuplot::plot_circle(double east, double north, double radius, const std::string &label)
1904 {
1905  std::ostringstream cmdstr;
1906  //
1907  // command to be sent to gnuplot
1908  //
1909  cmdstr << "set object circle at " + std::to_string(east) + "," + std::to_string(north) + " size " +
1910  std::to_string(radius) + " back\n";
1911 
1912  if (!label.empty())
1913  {
1914  double east_label = (std::cos(M_PI / 3.0) * radius) * 1.1 + east;
1915  double north_label = (std::sin(M_PI / 3.0) * radius) * 1.1 + north;
1916  cmdstr << "set label \"" + label + "\" at first " + std::to_string(east_label) +
1917  ", " + std::to_string(north_label) + " norotate back nopoint offset 0,0\n";
1918  }
1919  if (nplots > 0)
1920  {
1921  cmdstr << "replot ";
1922  }
1923  else
1924  {
1925  cmdstr << "plot ";
1926  }
1927 
1928  //
1929  // Do the actual plot
1930  //
1931  cmd(cmdstr.str());
1932 
1933  return *this;
1934 }
1935 
1936 
1937 // ----------------------------------------------------------------------------
1938 //
1939 // Sends a command to an active gnuplot session
1940 //
1941 inline Gnuplot &Gnuplot::cmd(const std::string &cmdstr)
1942 {
1943  if (!(valid))
1944  {
1945  return *this;
1946  }
1947 
1948  // int fputs ( const char * str, FILE * stream );
1949  // writes the string str to the stream.
1950  // The function begins copying from the address specified (str) until it
1951  // reaches the terminating null character ('\0'). This final
1952  // null-character is not copied to the stream.
1953  fputs((cmdstr + "\n").c_str(), gnucmd);
1954 
1955  // int fflush ( FILE * stream );
1956  // If the given stream was open for writing and the last i/o operation was
1957  // an output operation, any unwritten data in the output buffer is written
1958  // to the file. If the argument is a null pointer, all open files are
1959  // flushed. The stream remains open after this call.
1960  fflush(gnucmd);
1961 
1962  if (cmdstr.find("replot") != std::string::npos)
1963  {
1964  return *this;
1965  }
1966  if (cmdstr.find("splot") != std::string::npos)
1967  {
1968  two_dim = false;
1969  nplots++;
1970  }
1971  else if (cmdstr.find("plot") != std::string::npos)
1972  {
1973  two_dim = true;
1974  nplots++;
1975  }
1976 
1977  return *this;
1978 }
1979 
1980 
1981 // ----------------------------------------------------------------------------
1982 //
1983 // Opens up a gnuplot session, ready to receive commands
1984 //
1985 inline void Gnuplot::init()
1986 {
1987 // char * getenv ( const char * name ); get value of environment variable
1988 // Retrieves a C string containing the value of the environment variable
1989 // whose name is specified as argument. If the requested variable is not
1990 // part of the environment list, the function returns a NULL pointer.
1991 #if (defined(unix) || defined(__unix) || defined(__unix__)) && !defined(__APPLE__)
1992  if (std::getenv("DISPLAY") == nullptr)
1993  {
1994  valid = false;
1995  throw GnuplotException("Can't find DISPLAY variable");
1996  }
1997 #endif
1998 
1999  // if gnuplot not available
2000  if (!Gnuplot::get_program_path())
2001  {
2002  valid = false;
2003  throw GnuplotException("Can't find gnuplot");
2004  }
2005 
2006  //
2007  // open pipe
2008  //
2009  std::string tmp = Gnuplot::m_sGNUPlotPath + "/" +
2010  Gnuplot::m_sGNUPlotFileName;
2011 
2012 // FILE *popen(const char *command, const char *mode);
2013 // The popen() function shall execute the command specified by the string
2014 // command, create a pipe between the calling program and the executed
2015 // command, and return a pointer to a stream that can be used to either read
2016 // from or write to the pipe.
2017 #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__)
2018  gnucmd = _popen(tmp.c_str(), "w");
2019 #elif defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
2020  gnucmd = popen(tmp.c_str(), "w");
2021 #endif
2022 
2023  // popen() shall return a pointer to an open stream that can be used to read
2024  // or write to the pipe. Otherwise, it shall return a null pointer and may
2025  // set errno to indicate the error.
2026  if (!gnucmd)
2027  {
2028  valid = false;
2029  throw GnuplotException("Couldn't open connection to gnuplot");
2030  }
2031 
2032  nplots = 0;
2033  valid = true;
2034  smooth = "";
2035 
2036  // set terminal type
2037  showonscreen();
2038 
2039  return;
2040 }
2041 
2042 
2043 // ----------------------------------------------------------------------------
2044 //
2045 // Find out if a command lives in m_sGNUPlotPath or in PATH
2046 //
2047 inline bool Gnuplot::get_program_path()
2048 {
2049  //
2050  // first look in m_sGNUPlotPath for Gnuplot
2051  //
2052  std::string tmp = Gnuplot::m_sGNUPlotPath + "/" +
2053  Gnuplot::m_sGNUPlotFileName;
2054 
2055 #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__)
2056  if (Gnuplot::file_exists(tmp, 0)) // check existence
2057 #elif defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
2058  if (Gnuplot::file_exists(tmp, 1)) // check existence and execution permission
2059 #endif
2060  {
2061  return true;
2062  }
2063 
2064  //
2065  // second look in PATH for Gnuplot
2066  //
2067  const char *path;
2068  // Retrieves a C string containing the value of environment variable PATH
2069  path = std::getenv("PATH");
2070  std::stringstream s;
2071  if (path != nullptr)
2072  {
2073  s << path;
2074  }
2075  if (s.fail())
2076  {
2077  throw GnuplotException("PATH is not well defined");
2078  }
2079  std::string path_str;
2080  path_str = s.str();
2081 
2082  std::list<std::string> ls;
2083 
2084 // split path (one long string) into list ls of strings
2085 #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__)
2086  stringtok(ls, path_str, ";");
2087 #elif defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
2088  stringtok(ls, path_str, ":");
2089 #endif
2090 
2091  // scan list for Gnuplot program files
2092  for (std::list<std::string>::const_iterator i = ls.begin();
2093  i != ls.end(); ++i)
2094  {
2095  tmp = (*i) + "/" + Gnuplot::m_sGNUPlotFileName;
2096 #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__)
2097  if (Gnuplot::file_exists(tmp, 0)) // check existence
2098 #elif defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
2099  if (Gnuplot::file_exists(tmp, 1)) // check existence and execution permission
2100 #endif
2101  {
2102  Gnuplot::m_sGNUPlotPath = *i; // set m_sGNUPlotPath
2103  return true;
2104  }
2105  }
2106 
2107  tmp = "Can't find gnuplot neither in PATH nor in \"" +
2108  Gnuplot::m_sGNUPlotPath + "\"";
2109  Gnuplot::m_sGNUPlotPath = "";
2110  throw GnuplotException(tmp);
2111 }
2112 
2113 
2114 // ----------------------------------------------------------------------------
2115 //
2116 // check if file exists
2117 //
2118 inline bool Gnuplot::file_exists(const std::string &filename, int mode)
2119 {
2120  if (mode < 0 || mode > 7)
2121  {
2122  throw std::runtime_error(
2123  "In function \"Gnuplot::file_exists\": mode\
2124  has to be an integer between 0 and 7");
2125  return false;
2126  }
2127 
2128 // int _access(const char *path, int mode);
2129 // returns 0 if the file has the given mode,
2130 // it returns -1 if the named file does not exist or is not accessible in
2131 // the given mode
2132 // mode = 0 (F_OK) (default): checks file for existence only
2133 // mode = 1 (X_OK): execution permission
2134 // mode = 2 (W_OK): write permission
2135 // mode = 4 (R_OK): read permission
2136 // mode = 6 : read and write permission
2137 // mode = 7 : read, write and execution permission
2138 #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__)
2139  if (_access(filename.c_str(), mode) == 0)
2140 #elif defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
2141  if (access(filename.c_str(), mode) == 0)
2142 #endif
2143  {
2144  return true;
2145  }
2146  return false;
2147 }
2148 
2149 
2150 inline bool Gnuplot::file_available(const std::string &filename)
2151 {
2152  std::ostringstream except;
2153  if (Gnuplot::file_exists(filename, 0)) // check existence
2154  {
2155  if (!(Gnuplot::file_exists(filename, 4)))
2156  { // check read permission
2157  except << "No read permission for File \"" << filename << "\"";
2158  throw GnuplotException(except.str());
2159  return false;
2160  }
2161  }
2162  else
2163  {
2164  except << "File \"" << filename << "\" does not exist";
2165  throw GnuplotException(except.str());
2166  return false;
2167  }
2168  return true;
2169 }
2170 
2171 
2172 // ----------------------------------------------------------------------------
2173 //
2174 // Opens a temporary file
2175 //
2176 inline std::string Gnuplot::create_tmpfile(std::ofstream &tmp)
2177 {
2178 #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__)
2179  char name[] = "gnuplotiXXXXXX"; // tmp file in working directory
2180 #elif defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
2181  char name[] = "/tmp/gnuplotiXXXXXX"; // tmp file in /tmp
2182 #endif
2183 
2184  //
2185  // check if maximum number of temporary files reached
2186  //
2187  if (Gnuplot::tmpfile_num == GP_MAX_TMP_FILES - 1)
2188  {
2189  std::ostringstream except;
2190  except << "Maximum number of temporary files reached ("
2191  << GP_MAX_TMP_FILES << "): cannot open more files\n";
2192 
2193  throw GnuplotException(except.str());
2194  }
2195 
2196  // int mkstemp(char *name);
2197  // shall replace the contents of the string pointed to by "name" by a unique
2198  // filename, and return a file descriptor for the file open for reading and
2199  // writing. Otherwise, -1 shall be returned if no suitable file could be
2200  // created. The string in template should look like a filename with six
2201  // trailing 'X' s; mkstemp() replaces each 'X' with a character from the
2202  // portable filename character set. The characters are chosen such that the
2203  // resulting name does not duplicate the name of an existing file at the
2204  // time of a call to mkstemp()
2205 
2206  //
2207  // open temporary files for output
2208  //
2209 
2210 #if defined(WIN32) || defined(_WIN32) || defined(__WIN32__) || defined(__TOS_WIN__)
2211  if (_mktemp(name) == NULL)
2212 #elif defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
2213  mode_t mask = umask(S_IXUSR | S_IRWXG | S_IRWXO);
2214  if (mkstemp(name) == -1)
2215 #endif
2216  {
2217  std::ostringstream except;
2218  except << "Cannot create temporary file \"" << name << "\"";
2219 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
2220  umask(mask);
2221 #endif
2222  throw GnuplotException(except.str());
2223  }
2224 #if defined(unix) || defined(__unix) || defined(__unix__) || defined(__APPLE__)
2225  umask(mask);
2226 #endif
2227  tmp.open(name);
2228  if (tmp.bad())
2229  {
2230  std::ostringstream except;
2231  except << "Cannot create temporary file \"" << name << "\"";
2232  throw GnuplotException(except.str());
2233  }
2234 
2235  //
2236  // Save the temporary filename
2237  //
2238  tmpfile_list.emplace_back(name);
2239  Gnuplot::tmpfile_num++;
2240 
2241  return name;
2242 }
2243 
2244 
2245 inline void Gnuplot::remove_tmpfiles()
2246 {
2247  if (!(tmpfile_list).empty())
2248  {
2249  for (auto &i : tmpfile_list)
2250  {
2251  if (remove(i.c_str()) != 0)
2252  {
2253  std::cout << "Problem closing files\n";
2254  }
2255  }
2256 
2257  Gnuplot::tmpfile_num -= tmpfile_list.size();
2258  }
2259 }
2260 
2261 
2262 #endif
Gnuplot(const std::string &style="points")
set a style during construction
Definition: gnuplot_i.h:695
Gnuplot & unset_title()
Clears the title of a gnuplot session.
Definition: gnuplot_i.h:434
Gnuplot & replot(void)
replot repeats the last plot or splot command.
Definition: gnuplot_i.h:639
Gnuplot & operator<<(const std::string &cmdstr)
Sends a command to an active gnuplot session, identical to cmd()
Definition: gnuplot_i.h:225