Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing data from C++ to gnuplot example (using Gnuplot-iostream interface)

I have just come accross Dan Stahlke's gnuplot C++ I/O interface, which saves me from "rolling my own". Unfortunately, there are not too may examples and there ios no real documentation.

I have the following data types in my C++ project:

struct Data
{
  std::string datestr;  // x axis value
  float f1;             // y axis series 1
  float f2;             // y axis series 2
  float f3;             // y axis series 3
};


typedef std::vector<Data> Dataset;

I want to pass a Dataset variable from C++, so that I can plot the data (dates on the X axis, and the 3 numbers plotted as time series on the Y axis).

Can anyone show me how to transfer the Dataset variable from C++ to gnuplot (using the Gnuplot-iostream interface) and make a simple plot using the passed in data?

like image 513
skyeagle Avatar asked Jan 16 '11 12:01

skyeagle


2 Answers

I have recently pushed a new version to git which makes it easy to support custom datatypes such as this. To support your struct Data, you can provide a specialization of the TextSender class. Here is a complete example, using the struct you defined.

#include <vector>
#include "gnuplot-iostream.h"

struct Data {
    std::string datestr;  // x axis value
    float f1;             // y axis series 1
    float f2;             // y axis series 2
    float f3;             // y axis series 3
};

typedef std::vector<Data> Dataset;

namespace gnuplotio {
    template<>
    struct TextSender<Data> {
        static void send(std::ostream &stream, const Data &v) {
            TextSender<std::string>::send(stream, v.datestr);
            stream << " ";
            TextSender<float>::send(stream, v.f1);
            stream << " ";
            TextSender<float>::send(stream, v.f2);
            stream << " ";
            TextSender<float>::send(stream, v.f3);

            // This works too, but the longer version above gives
            // gnuplot-iostream a chance to format the numbers itself (such as
            // using a platform-independent 'nan' string).
            //stream << v.datestr << " " << v.f1 << " " << v.f2 << " " << v.f3;
        }
    };
}

int main() {
    Dataset x(2);
    // The http://www.gnuplot.info/demo/timedat.html example uses a tab between
    // date and time, but this doesn't seem to work (gnuplot interprets it as
    // two columns).  So I use a comma.
    x[0].datestr = "01/02/2003,12:34";
    x[0].f1 = 1;
    x[0].f2 = 2;
    x[0].f3 = 3;
    x[1].datestr = "02/04/2003,07:11";
    x[1].f1 = 10;
    x[1].f2 = 20;
    x[1].f3 = 30;

    Gnuplot gp;
    gp << "set timefmt \"%d/%m/%y,%H:%M\"\n";
    gp << "set xdata time\n";
    gp << "plot '-' using 1:2 with lines\n";
    gp.send1d(x);

    return 0;
}

A similar thing can be done to support sending the data in a binary format. See example-data-1d.cc from the git repo for an example.

Alternatively, custom datatypes like this can be supported by overriding operator<<(std::ostream &, ...).

Another option is to use std::tuple (available in C++11) or boost::tuple instead of defining your own struct. These are supported out of the box (well, now they are, they weren't at the time that you asked the question).

like image 85
Dan Stahlke Avatar answered Sep 21 '22 13:09

Dan Stahlke


Did you have a look the examples that come with gnuplot-iostream?

They are a bit sparse but they show how to create a plot from a series of data points:

Gnuplot gp;

gp << "set terminal png\n";

std::vector<double> y_pts;
for(int i=0; i<1000; i++) {
    double y = (i/500.0-1) * (i/500.0-1);
    y_pts.push_back(y);
}

gp << "set output 'my_graph_1.png'\n";
gp << "plot '-' with lines, sin(x/200) with lines\n";
gp.send(y_pts);
like image 42
Jay Avatar answered Sep 20 '22 13:09

Jay