StatusBarDecorator.cc 11.3 KB
/*
 * StatusBarDecorator.cc
 *
 *  Created on: Jan 12, 2015
 *      Author: AKKA
 */

#include "StatusBarDecorator.hh"

#include <iosfwd>
#include <boost/format.hpp>
#include <tuple>

#include "log4cxx/logger.h"
#include "plplot/plplot.h"
#include "plplot/plplotP.h"

#include "boost/exception/all.hpp"

#include "PlotLogger.hh"
#include "TimeAxis.hh"
#include "Page.hh"
#include "Time/TimePlot.hh"
#include "StatusPlot.hh"
#include "ColormapManager.hh"
#include "ParamMgr.hh"
#include "PlPlotUtil.hh"

#define BAR_HEIGHT 0.02
#define BAR_SPACE  0.005

namespace plot {

log4cxx::LoggerPtr StatusBarDecorator::_logger(
		log4cxx::Logger::getLogger("AMDA-Kernel.Plot.Time.StatusBarDecorator"));

StatusBarDecorator::StatusBarDecorator (AMDA::Parameters::ParameterManager& manager, bool isStandalone, PanelPlotOutput* decoratorPlot) :
		DefaultTimeAxisDecorator(),
		_parameterManager(manager),
		_colorMapIndex(ColormapManager::getInstance()._defaultColorAxis),
		_barPosition(PlotCommon::Position::POS_TOP),
		_isStandalone(isStandalone),
		_decoratorPlot(decoratorPlot){
}

StatusBarDecorator::StatusBarDecorator(const StatusBarDecorator& ref_) :
		DefaultTimeAxisDecorator(ref_), _parameterManager(
				ref_._parameterManager), _barInfoList(
				ref_._barInfoList), _colorMapIndex(ref_._colorMapIndex),
				_barPosition(ref_._barPosition), _isStandalone(ref_._isStandalone),
				_decoratorPlot(ref_._decoratorPlot) {
}

StatusBarDecorator& StatusBarDecorator::operator=(const StatusBarDecorator& ref_) {
	_parameterManager = ref_._parameterManager;
	_barInfoList = ref_._barInfoList;
	_colorMapIndex = ref_._colorMapIndex;
	_barPosition = ref_._barPosition;
	_isStandalone = ref_._isStandalone;
	_decoratorPlot = ref_._decoratorPlot;
	return *this;
}

StatusBarDecorator::~StatusBarDecorator() {
}

void StatusBarDecorator::updatePlotArea(PanelPlotOutput* pplot_, Axis* axis_, Bounds& bounds_) {
	//Space for time axis
	DefaultTimeAxisDecorator::updatePlotArea(pplot_, axis_, bounds_);

	LOG4CXX_DEBUG(_logger, "StatusBarDecorator Updating plot area : "<<bounds_.toString());

	TimeAxis* pAxis = (TimeAxis*) axis_;

	// calculate char size for labels:
	Font legendFont(pAxis->_legend.getFont());
	Font panelFont(pplot_->_panel->getFont());
	if (!legendFont.isSet()) {
		legendFont = panelFont;
	}

	PlPlotUtil::setPlFont(legendFont);
	CharSize legendSize = PlPlotUtil::getCharacterSizeInPlPage(pplot_->_panel->_page);

	double barSpace = 0;

	// reserve space for all series bar :
	barSpace += ((BAR_HEIGHT + BAR_SPACE + 1.5*legendSize.second) * _barInfoList.size());

	if ((_barPosition == pAxis->_position) && (_barPosition == PlotCommon::POS_BOTTOM))
		//one additional space to not draw the bar too close to the date
		barSpace += legendSize.second;

	// to take into account bar space.
	bounds_._height -= barSpace;

	if (_isStandalone)
		bounds_._height = BAR_SPACE;

	// specific case when axis labels are displayed at bottom of the graph. Needs to update y.
	if (_barPosition == PlotCommon::POS_BOTTOM)
	{
		bounds_._y += barSpace;
	}
}

double StatusBarDecorator::computeStartDateWidth(PanelPlotOutput* pplot_,
		TimeAxis* pAxis_) {
	// not very efficient ...
	long int lTime = static_cast<long int>(pAxis_->getRange().getMin());
	tm * lTimeTm = gmtime(&lTime);
	char lTimeChr[80];

	// Format date.
	size_t datelen =
			strftime(lTimeChr, 80,
					getPlStartTimeFormat(pAxis_->_timeFormat,
							pAxis_->getRange().getMin(),
							pAxis_->getRange().getMax()).c_str(), lTimeTm);
	PlPlotUtil::setPlFont(pplot_->_panel->getFont());
	CharSize fontSize = PlPlotUtil::getCharacterSizeInPlPage(pplot_->_panel->_page);
	return fontSize.first * datelen;
}

void StatusBarDecorator::configure(PanelPlotOutput* pplot_, Axis* axis_, double start_,
		double stop_, std::map<std::string, ParameterData> *pParameterValues) {

	TimeAxis* pTimeAxis = (TimeAxis*) axis_;

	if (_isStandalone)
		pTimeAxis->setOnlyTickmarks(true);

	DefaultTimeAxisDecorator::configure(pplot_,axis_,start_,stop_,pParameterValues);

	LOG4CXX_DEBUG(_logger, "StatusBarDecorator::configure");
	buildBarList();
}

void StatusBarDecorator::draw(PanelPlotOutput* pplot_, Axis* axis_,
		std::shared_ptr<plstream> pls_) {
	DefaultTimeAxisDecorator::draw(pplot_,axis_,pls_);

	// draw a virtual axis for each entry // take into account plot orientation
	TimeAxis* pAxis = (TimeAxis*) axis_;

	// Get plot area viewport.
	Bounds vpBounds;
	PLFLT lXMin, lXMax, lYMin, lYMax;
	pls_->gvpd(lXMin, lXMax, lYMin, lYMax);
	vpBounds._x = lXMin;
	vpBounds._y = lYMin;
	vpBounds._width = lXMax - lXMin;
	vpBounds._height = lYMax - lYMin;

	// get windows
	Bounds winBounds;
	PLFLT xwmin, xwmax, ywmin, ywmax;
	pls_->gvpw(xwmin, xwmax, ywmin, ywmax);
	winBounds._x = xwmin;
	winBounds._y = ywmin;
	winBounds._width = xwmax - xwmin;
	winBounds._height = ywmax - ywmin;

	LOG4CXX_DEBUG(_logger, " viewport :"<<vpBounds.toString());
	LOG4CXX_DEBUG(_logger, " window :"<<winBounds.toString());

	// set color for drawing axis :
	Color lInitialColor = changeColor(pls_, pAxis->_color,
			pplot_->_panel->_page->_mode);

	// Set font to draw axes.
	std::vector<Font::Style> panelStyles;
	Font panelFont(pplot_->_panel->getFont());

	Font legendFont(pAxis->_legend.getFont());
	if (!legendFont.isSet()) {
		// font has not been defined for that axis, used default panel font :
		legendFont = panelFont;
	}


	// viewport translation only along y direction: we translate about n charHeight. So, gets panel character height.
	PlPlotUtil::setPlFont(panelFont);
	CharSize panelCharSize = PlPlotUtil::getCharacterSizeInPlPage(pplot_->_panel->_page);

	PlPlotUtil::setPlFont(legendFont);
	CharSize legendCharSize = PlPlotUtil::getCharacterSizeInPlPage(pplot_->_panel->_page);

	double delta = BAR_SPACE;

	//add space for tick if necessary
	if (pAxis->_visible && pAxis->_showTickMark && !_isStandalone && (pAxis->_tick._position == Tick::TickPosition::OUTWARDS))
		delta += getVerticalTickLength(pAxis, panelCharSize.second);

	//add space for time axis
	if (pAxis->_visible && (_barPosition == pAxis->_position) && pAxis->_showLegend && !pAxis->_legend.isEmpty())
	{
		delta += 3*panelCharSize.second;
	}

	//keep status bar order
	int first = 0;
	int end   = _barInfoList.size();
	int dir   = 1;
	if (_barPosition == PlotCommon::Position::POS_TOP)
	{
		first = _barInfoList.size()-1;
		end   = -1;
		dir   = -1;
	}

	//add status bar
	for (int i = first; i != end; i+=dir)
	{
		LOG4CXX_DEBUG(_logger, "statusbar["<<i<<"]");
		// no generate label
		pls_->slabelfunc(generateNoTimeLabel, NULL);
		// translate viewport :
		if (_barPosition == PlotCommon::Position::POS_BOTTOM)
		{
			pls_->vpor(vpBounds._x, vpBounds._x + vpBounds._width,
					vpBounds._y - delta - 1.5*legendCharSize.second - BAR_HEIGHT,
					vpBounds._y - delta - 1.5*legendCharSize.second);
		}
		else
		{
			pls_->vpor(vpBounds._x, vpBounds._x + vpBounds._width,
				vpBounds._y + vpBounds._height + delta ,
				vpBounds._y + vpBounds._height + delta + BAR_HEIGHT);
		}
		// sets windows for new viewport :
		pls_->wind(winBounds._x, winBounds._x + winBounds._width, 0,
				1);

		ParamInfoSPtr paramInfo = _barInfoList[i]._paramInfoSPtr;

		//Working list of pair used to build the legend
		std::vector<std::pair<int,std::string>> legend;

		//legend configuration
		if ((paramInfo != nullptr) && !paramInfo->getStatusDef().empty())
		{
			for (int s = 0 ; s < (int)paramInfo->getStatusDef().size(); ++s)
			{
				legend.push_back(std::make_pair(s,paramInfo->getStatusDef()[s].getName()));
			}
		}

		//Draw Status data
		PanelPlotOutput::MatrixGrid matrixGrid;
		for (int t = 0; t < _barInfoList[i]._dataPtr->getSize() - 1;++t)
		{
			PanelPlotOutput::GridPart part;
			part.x[0] = _barInfoList[i]._dataPtr->getTimes()[t];
			part.x[1] = _barInfoList[i]._dataPtr->getTimes()[t+1];
			part.y[0] = 0;
			part.y[1] = 1;

			double paramValue = _barInfoList[i]._dataPtr->getValues(_barInfoList[i]._index)[t];

			if (isNAN(paramValue))
				part.value = NAN;
			else
			{
				if ((paramInfo != nullptr) && !paramInfo->getStatusDef().empty())
				{
					bool valid = false;
					for (int s = 0 ; s < (int)paramInfo->getStatusDef().size(); ++s)
					{
						if ((paramValue >= paramInfo->getStatusDef()[s].getMinValue()) &&
							(paramValue <= paramInfo->getStatusDef()[s].getMaxValue()))
						{
							valid = true;
							part.value = s;
							break;
						}
					}

					if (!valid)
						part.value = NAN;
				}
				else
					//status definition is given by the paramInfo
					part.value = NAN;
			}

			matrixGrid.push_back(part);
		}

		Color minValColor;
		Color maxValColor;

		//draw spectro
		pplot_->drawMatrix(matrixGrid,
				_barInfoList[i]._dataPtr->getMin(),
				_barInfoList[i]._dataPtr->getMin(),
				minValColor, maxValColor, _colorMapIndex, true);

		//draw bar box
		pls_->box("bc", 0, 0, "bc", 0, 0);

		//draw legend
		PlPlotUtil::setPlFont(legendFont);
		std::string legendText = _barInfoList[i]._name;
		legendText += ": ";
		//plot parameter name
		pls_->mtex("t",1,0,0,legendText.c_str());

		PLFLT xmin,xmax,ymin,ymax;
		pls_->gspa(xmin,xmax,ymin,ymax);

		double textFactor = (xmax-xmin) * vpBounds._width ;

		double legendPos = plstrl(legendText.c_str()) / textFactor;
		std::string separator = ", ";
		//plot each status name with the associated color
		for (int p = 0; p < (int)legend.size(); ++p)
		{
			Color partColor(_colorMapIndex, (int)legend[p].first);
			Color initColor = changeColor(pls_, partColor, pplot_->_panel->_page->_mode);
			pls_->mtex("t",1,legendPos,0,legend[p].second.c_str());
			legendPos += plstrl(legend[p].second.c_str()) / textFactor;
			restoreColor(pls_, initColor, pplot_->_panel->_page->_mode);
			if (p != (int)legend.size() - 1)
			{
				pls_->mtex("t",1,legendPos,0,separator.c_str());
				legendPos += plstrl(separator.c_str()) / textFactor;
			}
		}

		delta += (BAR_HEIGHT+BAR_SPACE+1.5*legendCharSize.second);
	}

	// restore viewport.
	pls_->vpor(vpBounds._x, vpBounds._x + vpBounds._width, vpBounds._y,
			vpBounds._y + vpBounds._height);

	// restore window
	pls_->wind(winBounds._x, winBounds._x + winBounds._width, winBounds._y,
			winBounds._y + winBounds._height);

	// Restore initial color.
	restoreColor(pls_, lInitialColor, pplot_->_panel->_page->_mode);

}

void StatusBarDecorator::buildBarList() {
	LOG4CXX_DEBUG(_logger, "StatusBarDecorator::buildBarList");

	// Retrieve ParamInfo Manager
	ParamMgr 		*piMgr =ParamMgr::getInstance();

	_barInfoList.clear();
	for (auto p : _decoratorPlot->_parameterAxesList)
	{
		ParameterSPtr param  = _parameterManager.getParameter(p._originalParamId);
		ParamInfoSPtr paramInfo = piMgr->getParamInfoFromId(param->getInfoId());

		std::string name;

		if ((paramInfo == nullptr) || (paramInfo->getShortName().empty()))
			name = p._originalParamId;
		else
			name = paramInfo->getShortName();

		LOG4CXX_DEBUG(_logger, " inspecting parameter : " << name);
		// list asked series :
		for (auto q : p.getYSerieIndexList(_pParameterValues)) {
			BarInfo barInfo;

			std::ostringstream osstr;
			osstr << name;
			// display index only if non scalar
			if(q.getDim1Index() != -1){
				osstr << "[" << q.getDim1Index() ;
				if (q.getDim2Index() != -1)
					osstr << "," << q.getDim2Index();
				osstr << "]";
			}
			LOG4CXX_DEBUG(_logger, " Display name : " << osstr.str());

			barInfo._name = osstr.str();
			barInfo._index = q;
			barInfo._dataPtr = &(*_pParameterValues)[p.getYSeriePropertiesAt(q).getParamId()];
			barInfo._paramInfoSPtr = paramInfo;
			_barInfoList.push_back(barInfo);
		}
	}
}

} /* namespace plot */