Search code examples
c++graphvisualization

pbPlots graph png file is unreadable C++


I have recently been working on a simple SIR Epidemic Simulation, and wanted to use pbPlots to visualize the data. Unfortunately, I can't get it to work correctly. Every time I run my code and go to open the png file, it just says that the file is unreadable or corrupted. I also noticed that the file size is only 65 bytes, which is smaller than the usual 180KB.

Here's my code:

/*
    Brandon Pyle
    SIR Epidemic Simulation

    This program is a simple customizable epidemic simulation that uses the SIR model
*/

//Include statements
#include <string>
#include <iostream>
#include <iomanip>
#include <vector>
#include "pbPlots.hpp"
#include "supportLib.hpp"

using namespace std;

//Function prototypes

int main()
{
    //The following block of code creates the title box in the command line
    for (int i = 0; i < 50; i++)
        cout << "*";
    cout << endl << setw(49) << left << "*" << right << "*" << endl;
    cout << "*                  Brandon Pyle                  *" << endl;
    cout << "*            SIR Epidemic Simulation             *";
    cout << endl << setw(49) << left << "*" << right << "*" << endl;
    for (int i = 0; i < 50; i++)
        cout << "*";

    cout << endl << endl;

    //Variable Declarations
    int numDays;
    int population;
    double infectionRate = 0.0002; //Percent of other people a person can infect
    double recoveryRate = 10; //In days

    cout << "Enter the length of the simulation in days: ";
    cin >> numDays;

    cout << endl << "Enter the population number for the simulation: ";
    cin >> population;

    if (population > 5000)
        cout << "WARNING: Numbers greater than 5000 may result in incorrect or inaccurate results." << endl;
    else
        cout << endl;

    vector<double> S(population, 0.0); //Number of Susceptible People
    vector<double> I(population, 0.0); //Number of Infected People
    vector<double> R(population, 0.0); //Number of Removed People
    
    //Creates variables for the X-Axis that are used for pbPlots
    vector<double> xPos;
    for (double i = 0.0; i < numDays; i++)
        xPos.push_back(i);

    I[0] = 1; //Starts the simulation with 6 infected people
    S[0] = population - I[0]; //Initial number of susceptible people
    R[0] = 0; //Initial number of removed people

    RGBABitmapImageReference* imageReference = CreateRGBABitmapImageReference();

    cout << setw(5) << right << "Day";
    cout << setw(13) << right << "Susceptible";
    cout << setw(10) << right << "Infected";
    cout << setw(9) << right << "Removed" << endl;

    for (int i = 0; i < numDays; i++)
    {
        cout << setw(5) << right << i + 1;
        cout << setw(13) << right << fixed << setprecision(0) << S[i];
        cout << setw(10) << right << fixed << setprecision(0) << I[i];
        cout << setw(9) << right << fixed << setprecision(0) << R[i] << endl << endl;

        S[i + 1] = S[i] - infectionRate * S[i] * I[i];
        I[i + 1] = I[i] + infectionRate * S[i] * I[i] - I[i] / recoveryRate;
        R[i + 1] = R[i] + I[i] / recoveryRate;
    }

    ScatterPlotSeries* series = GetDefaultScatterPlotSeriesSettings();
    series->xs = &xPos;
    series->ys = &S;
    series->linearInterpolation = false;
    series->lineType = toVector(L"solid");
    series->color = CreateRGBColor(0, 0, 1);
    
    ScatterPlotSeries* series2 = GetDefaultScatterPlotSeriesSettings();
    series->xs = &xPos;
    series->ys = &I;
    series->linearInterpolation = false;
    series->lineType = toVector(L"solid");
    series->color = CreateRGBColor(0, 1, 0);

    ScatterPlotSeries* series3 = GetDefaultScatterPlotSeriesSettings();
    series->xs = &xPos;
    series->ys = &R;
    series->linearInterpolation = false;
    series->lineType = toVector(L"solid");
    series->color = CreateRGBColor(0, 0, 0);

    ScatterPlotSettings *settings = GetDefaultScatterPlotSettings();
    settings->width = 800;
    settings->height = 480;
    settings->autoBoundaries = true;
    settings->autoPadding = true;
    settings->title = toVector(L"SIR Epidemic Simulation");
    settings->xLabel = toVector(L"Days");
    settings->yLabel = toVector(L"Population");
    settings->scatterPlotSeries->push_back(series);
    settings->scatterPlotSeries->push_back(series2);
    settings->scatterPlotSeries->push_back(series3);

    DrawScatterPlotFromSettings(imageReference, settings);

    //DrawScatterPlot(imageReference, 800, 480, &I, &S);
    
    vector<double>* pngData = ConvertToPNG(imageReference->image);
    WriteToFile(pngData, "SIR_Graph_Test.png");
    DeleteImage(imageReference->image);

    return 0;
}

I have narrowed down the issue to be something with the xPos vector, but I can't figure out why it isn't working. Also, the program does work if you comment out all of the ScatterPlotSeries stuff and uncomment the DrawScatterPlot(...); line. This method works because it uses the I vector for the X axis instead of my xPos vector.

Any suggestions?

Here is the Minimum Reproducible Example:

//Include statements
#include <vector>
#include "pbPlots.hpp"
#include "supportLib.hpp"

using namespace std;

int main()
{
    vector<double> xPos;
    for (double i = 0.0; i < 200; i++)
        xPos.push_back(i);

    vector<double> yPos;
    for (double i = 0.0; i < 200; i++)
        yPos.push_back(pow(i, 2));

    vector<double> yPos2;
    for (double i = 0.0; i < 200; i++)
        yPos2.push_back(i + 2);

    RGBABitmapImageReference* imageReference = CreateRGBABitmapImageReference();

    ScatterPlotSeries* series = GetDefaultScatterPlotSeriesSettings();
    series->xs = &xPos;
    series->ys = &yPos;
    series->linearInterpolation = false;
    series->lineType = toVector(L"solid");
    series->color = CreateRGBColor(0, 0, 1);

    ScatterPlotSeries* series2 = GetDefaultScatterPlotSeriesSettings();
    series->xs = &xPos;
    series->ys = &yPos2;
    series->linearInterpolation = false;
    series->lineType = toVector(L"solid");
    series->color = CreateRGBColor(0, 1, 0);

    ScatterPlotSettings* settings = GetDefaultScatterPlotSettings();
    settings->width = 800;
    settings->height = 480;
    settings->autoBoundaries = true;
    settings->autoPadding = true;
    settings->title = toVector(L"SIR Epidemic Simulation");
    settings->xLabel = toVector(L"Days");
    settings->yLabel = toVector(L"Population");
    settings->scatterPlotSeries->push_back(series);
    settings->scatterPlotSeries->push_back(series2);

    //Comment out the line below when using the working method
    DrawScatterPlotFromSettings(imageReference, settings);

    //Ucomment the line below to get a working graph
    //DrawScatterPlot(imageReference, 800, 480, &xPos, &yPos);

    vector<double>* pngData = ConvertToPNG(imageReference->image);
    WriteToFile(pngData, "SIR_Graph_Test_2.png");
    DeleteImage(imageReference->image);

    return 0;
}

Here is an image too compare the working PNG (right) to the corrupted PNG (left)

PNG Comparison


Solution

  • The problem is simply that you forgot to set the properties of series2. Look at your original code above, it sets the properties of "series" two times. If you change series-> to series2-> the second time, it works.