PlotFunction.cc 13 KB
#include <boost/lexical_cast.hpp>
#include <fstream>
#include <iomanip>

#include "AxesNode.hh"
#include "AxisLegendManager.hh"
#include "ParamMgr.hh"
#include "ParamsNode.hh"
#include "PlPlotUtil.hh"
#include "PlotFunction.hh"
#include "PlotLogger.hh"
#include "PlotOutput.hh"
#include "TimeUtil.hh"
#include "fonctions/fourier/DiscreteFourierTransform.cc"
#include "fonctions/fourier/DiscreteFourierTransform.hh"
#include <algorithm>
#include <limits>
#include <numeric>

#include "DefaultTimeAxisDecorator.hh"

#include "TimeAxisDecorator.hh"

using namespace AMDA::Parameters;
using namespace AMDA::Info;

namespace plot
{
    QSASConfig *PlotFunction::qsasconfig = NULL;

    PlotFunction::PlotFunction(AMDA::Parameters::ParameterManager &manager,
                               boost::shared_ptr<Panel> panel, TimeAxisDecorator *timeAxisDecorator) : PanelPlotOutput(manager, panel), function(PlotFunction::Function::NONE), abscisse("Time. ", "(s)", Abscisse::Abscisse_Type::TIME)

    {
        LOG4CXX_DEBUG(gLogger, "Function to apply " << function);
        setTimeAxisDecorator(std::shared_ptr<TimeAxisDecorator>(timeAxisDecorator));
    }

    PlotFunction::~PlotFunction()
    {
        if (qsasconfig != NULL)
        {
            free(qsasconfig);
            qsasconfig = nullptr;
        }
    }

    void PlotFunction::drawSeries(double startDate, double stopDate, int intervalIndex, std::string pParamId,
                                  SeriesProperties &pSerie,
                                  AMDA::Common::ParameterIndexComponent pParamIndex,
                                  ParameterAxes &param, bool moreThanOneSerieForAxis)
    {
        std::string id = std::to_string(pSerie.getId()) + "_" + pSerie.getParamId() + "_" + std::to_string(pParamIndex.getDim1Index());
        std::vector<double> xValues = xValuesMap[id];
        std::vector<double> yValues = yValuesMap[id];

        double *coloredComputedValues = NULL;
        int nbValues = yValues.size();

        double *yValuesTemp = &yValues[0];
        double *xValuesTemp = &xValues[0];

        pSerie.setXAxisId(X_AXIS_ID);
        pSerie.setYAxisId(Y_AXIS_ID);

        PanelPlotOutput::drawSeries(startDate, stopDate, intervalIndex, pParamId,
                                    pSerie, pParamIndex, param, moreThanOneSerieForAxis);
        // draw serie
        Color lineColor = getSerieLineColor(pSerie, moreThanOneSerieForAxis);
        Color symbolColor = getSerieSymbolColor(pSerie, lineColor);

        drawSymbols(
            pSerie.getSymbolProperties().getType(),
            pSerie.getSymbolProperties().getSize(), 1.,
            symbolColor,
            nbValues, xValuesTemp, yValuesTemp, coloredComputedValues);

        drawLines(
            pSerie.getLineProperties().getType(),
            pSerie.getLineProperties().getStyle(),
            pSerie.getLineProperties().getWidth(),
            lineColor,
            nbValues, xValuesTemp, yValuesTemp, coloredComputedValues);
    }

    void PlotFunction::configureSeriesAxis()
    {
        //_panel->_leftMargin = 10;
        //_panel->_rightMargin = 5;
        //_panel->_bottomMargin = 4;

        SeriesProperties lSeries;
        for (auto parameter : _parameterAxesList)
        {
            for (auto lSeries : parameter.getYSeriePropertiesList())
            {
                for (auto lIndex : lSeries.getIndexList(_pParameterValues))
                {
                    std::string id = std::to_string(lSeries.getId()) + "_" + lSeries.getParamId() + "_" + std::to_string(lIndex.getDim1Index());
                    plot::ParameterData data = (*_pParameterValues)[lSeries.getParamId()];
                    PlotFunction::compute(lIndex, data, id, lSeries.getParamId());
                }
            }
        }

        PlotFunction::createXAxis();
        PlotFunction::createYAxis();
    }

    void PlotFunction::createXAxis()
    {
        double minValue = 0;
        double maxValue = 0;
        PlotFunction::getMinMax(xValuesMap, &minValue, &maxValue);
        // Create X axis
        boost::shared_ptr<TimeAxis> lXAxis(new TimeAxis());
        lXAxis.get()->_timeFormat = "%H:%M";
        plot::Range range_x = Range(minValue, maxValue);
        lXAxis.get()->setRange(range_x);
        lXAxis.get()->_drawn = false;
        lXAxis.get()->_position = PlotCommon::Position::POS_BOTTOM;
        Label label1 = Label();
        // lXAxis.get()->setLegendOffset(0.03);
        label1._text = abscisse.getLabel() + abscisse.getUnit();
        lXAxis.get()->_legend.pushLabel(label1);
        lXAxis.get()->_scale = abscisseScale;
        lXAxis.get()->_showLegend = true;
        lXAxis.get()->_showTickMark = true;
        lXAxis.get()->_used = true;

        //  add X Axis to panel
        _panel->addAxis(X_AXIS_ID, lXAxis);
    }

    void PlotFunction::createYAxis()
    {
        std::string y_label = "";
        if (function == PlotFunction::Function::AVG)
            y_label = "AVG";
        else
            y_label = "Amplitude";

        double minValue = 0;
        double maxValue = 0;

        PlotFunction::getMinMax(yValuesMap, &minValue, &maxValue);
        // Create X axis
        boost::shared_ptr<Axis> lYAxis(new Axis(false));
        plot::Range range_x = Range(minValue - abs(minValue) * 0.10, maxValue + abs(maxValue) * 0.1);
        lYAxis.get()->setRange(range_x);
        lYAxis.get()->_drawn = false;
        lYAxis.get()->_position = PlotCommon::Position::POS_LEFT;
        Label label1 = Label();
        // lYAxis.get()->setLegendOffset(0.05);
        label1._text = y_label;
        lYAxis.get()->_legend.pushLabel(label1);
        lYAxis.get()->_scale = ordonneeScale;
        lYAxis.get()->_showLegend = true;
        lYAxis.get()->_showTickMark = true;
        lYAxis.get()->_used = true;
        //  add X Axis to panel
        _panel->addAxis(Y_AXIS_ID, lYAxis);
    }

    void PlotFunction::getMinMax(std::map<std::string, std::vector<double>> dataMap, double *minToFill, double *maxToFill)
    {
        double maxValue = INT_MIN;
        double minValue = INT_MAX;
        std::map<std::string, std::vector<double>>::iterator it;
        for (it = dataMap.begin(); it != dataMap.end(); it++)
        {
            std::vector<double> data = it->second;
            double max_x = *max_element(data.begin(), data.end());
            double min_x = *min_element(data.begin(), data.end());
            if (max_x > maxValue)
                maxValue = max_x;
            if (min_x < minValue)
                minValue = min_x;
        }
        *minToFill = minValue;
        *maxToFill = maxValue;
    }

    void PlotFunction::compute(AMDA::Common::ParameterIndexComponent pParamIndex, plot::ParameterData data, std::string id, std::string param_id)
    {
        double samplingValue = 60.0;
        int nb_points = data.getSize();

        auto it = find(paramsNbPoints.begin(), paramsNbPoints.end(), param_id);
        // If parameter was found
        if (it != paramsNbPoints.end())
        {
            int index = it - paramsNbPoints.begin();
            samplingValue = stod(paramsNbPoints[index + 1]);
            nb_points = stoi(paramsNbPoints[index + 2]);
        }
        else
        {
            std::stringstream lError;
            lError << "PlotFunction::compute"
                   << ": param with id " << param_id << " is not found in param_nb_points xml node.";
            LOG4CXX_DEBUG(gLogger, lError.str());
            BOOST_THROW_EXCEPTION(PanelPlotOutputException() << AMDA::errno_code(AMDA_ERROR_PLOTFUNCTION_PARAM_NOT_FOUND) << AMDA::ex_msg(lError.str()));
        }

        double *values = data.getValues(pParamIndex, 0);

        double *timeValues = &data.getTimes()[0];

        std::vector<double> signal;
        for (int i = 0; i < data.getSize(); i++)
            signal.push_back(values[i]);

        std::vector<double> xValues;
        std::vector<double> yValues;

        if (function == PlotFunction::Function::AVG)
        {
            double sum = std::accumulate(signal.begin(), signal.end(), 0);
            for (int i = 0; i < data.getSize(); i++)
            {
                yValues.push_back(sum / data.getSize());
                xValues.push_back(timeValues[i]);
            }
        }
        else
        {
            DiscreteFourierTransform<double, double> DFT(std::min(nb_points, data.getSize()), signal, 1 / samplingValue);
            DFT.compute();

            yValues = DFT.getAmplitudes();

            if (abscisse.getType() == Abscisse::Abscisse_Type::FREQUENCY)
                xValues = DFT.getFrequences();
            else
                xValues = DFT.getPeriodes();
        }

        if (ordonneeScale == Axis::Scale::LOGARITHMIC)
        {
            std::for_each(yValues.begin(), yValues.end(), [&](double &i)
                          { return log10(i); });
        }

        if (abscisseScale == Axis::Scale::LOGARITHMIC)
        {
            std::for_each(xValues.begin(), xValues.end(), [&](double &i)
                          { return log10(i); });
        }

        PlotFunction::xValuesMap[id] = xValues;
        PlotFunction::yValuesMap[id] = yValues;
    }

    void PlotFunction::configureAxisLegend()
    {
        // Y axis
        AxisLegendManager::configureYAxisLegendForSpectro(this);
        AxisLegendManager::configureYAxisLegendForSeries(this);

        // Z axis
        AxisLegendManager::configureColorAxisLegendForSpectro(this);
        AxisLegendManager::configureColorAxisLegendForSeries(this);
    }

    void PlotFunction::drawStartDate(std::string _timeFormat, double startTime, double stopTime)
    {
        Font font(_panel->getFont());
        Bounds lPanelBounds(_panel->getBoundsInPlPage());

        PLFLT lXMin, lXMax, lYMin, lYMax;
        _pls->gvpd(lXMin, lXMax, lYMin, lYMax);

        float lPosition = 0.0;
        // display it one line above the main panel bottom border :
        float disp = -1;

        long int lTime = static_cast<long int>(startTime);
        tm *lTimeTm = gmtime(&lTime);
        char lTimeChr[80];

        // Format date.
        strftime(lTimeChr, 80,
                 getPlStartTimeFormat(_timeFormat,
                                      startTime,
                                      stopTime)
                     .c_str(),
                 lTimeTm);

        PLFLT mxmin, mxmax, mymin, mymax;
        plgspa(&mxmin, &mxmax, &mymin, &mymax);
        float x_subpage_per_mm = 1. / (mxmax - mxmin);

        LOG4CXX_DEBUG(gLogger, "Start date to draw : " << lTimeChr);

        // Set font
        PlPlotUtil::setPlFont(font);

        PLFLT dateWidthInMm;
        dateWidthInMm = plstrl(lTimeChr);

        // set viewport for start date :
        _pls->vpor(lXMin, lXMax, lPanelBounds._y, lPanelBounds._y + lPanelBounds._height);

        lPosition = 0.;
        if (dateWidthInMm * x_subpage_per_mm > lXMin - lPanelBounds._x)
            lPosition = (dateWidthInMm * x_subpage_per_mm - lXMin + lPanelBounds._x) / (lXMax - lXMin);
        _pls->mtex("b", disp, lPosition, 1., lTimeChr);

        // restore viewport :
        _pls->vpor(lXMin, lXMax, lYMin, lYMax);
    }

    void PlotFunction::preparePlotArea(double startTime, double stopTime, int intervalIndex)
    {
        PlotFunction::configureSeriesAxis();

        if (abscisse.getType() == Abscisse::TIME)
        {
            boost::shared_ptr<Axis> xAxis = _panel->getAxis(X_AXIS_ID);
            getTimeAxisDecorator()->configure(this, dynamic_cast<TimeAxis *>(xAxis.get()), startTime, stopTime, _pParameterValues);
            _pls->timefmt(getTimeAxisDecorator()->getPlFormat().c_str());
        }
        PanelPlotOutput::preparePlotArea(startTime, stopTime, intervalIndex);
    }

    void PlotFunction::createParameters(std::list<std::string> &usedParametersId_)
    {

        for (ParameterAxesList::iterator it = _parameterAxesList.begin();
             it != _parameterAxesList.end(); ++it)
        {
            AMDA::Parameters::ParameterSPtr originalParam =
                _parameterManager.getParameter(it->_originalParamId);

            // For each series
            std::vector<SeriesProperties>::iterator ity;
            for (ity = it->getYSeriePropertiesList().begin(); ity != it->getYSeriePropertiesList().end();
                 ++ity)
            {
                // Add  parameter to parameters list
                if (std::find(usedParametersId_.begin(), usedParametersId_.end(),
                              originalParam->getId()) == usedParametersId_.end())
                    usedParametersId_.push_back(originalParam->getId());

                // link this paramter to the serie
                ity->setParamId(originalParam->getId());
            }
        }
    }

    /**
     * @brief draw the plot for the current time interval
     */
    bool PlotFunction::draw(double startTime, double stopTime, int intervalIndex,
                            bool isFirstInterval, bool isLastInterval)
    {
        bool out = PanelPlotOutput::draw(startTime, stopTime, intervalIndex, isFirstInterval, isLastInterval);

        if (abscisse.getType() == Abscisse::TIME)
        {
            std::string _timeFormat = "hh:mm:ss";
            PlotFunction::drawStartDate(_timeFormat, startTime, stopTime);
        }

        return out;
    }
}