EpochPlot.cc 12.8 KB
/*
 * EpochPlot.cc
 *
 *  Created on: 16 jan. 2015
 *      Author: AKKA
 */

#include "EpochPlot.hh"
#include "ParamsNode.hh"
#include "AxesNode.hh"
#include "PlotOutput.hh"
#include "TimeUtil.hh"
#include "Parameter.hh"
#include "ParamInfo.hh"
#include "ParamMgr.hh"
#include <fstream>

#include "EpochAxisDecorator.hh"
#include "TickMarkDecorator.hh"
#include "PlotLogger.hh"
#include "ParamMgr.hh"
#include "TimeUtil.hh"
#include "AxisLegendManager.hh"

#include <boost/format.hpp>

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

namespace plot {

EpochPlot::EpochPlot(AMDA::Parameters::ParameterManager& manager,
		boost::shared_ptr<Panel> panel) :
		PanelPlotOutput(manager, panel),
		_centerTimeId(""){
	_epochDecoratorPtr.reset(new EpochAxisDecorator());
}

EpochPlot::~EpochPlot() {
}


EpochAxis* EpochPlot::getEpochAxis(){
	std::string lAxisId = getXAxisId();
	boost::shared_ptr<Axis> xAxis = _panel->getAxis(lAxisId);
	if (xAxis.get() == nullptr) {
		std::stringstream lError;
		lError << "EpochPlot::apply" << ": epoch axis with id '" << lAxisId << "' not found.";
		BOOST_THROW_EXCEPTION(PanelPlotOutputException() << AMDA::ex_msg(lError.str()));
	}
	return dynamic_cast<EpochAxis*>(xAxis.get());
}

void EpochPlot::preparePlotArea(double startTime, double stopTime, int intervalIndex) {
	// dump properties for test
	const char* lBuildType=getenv("BUILD_TYPE");
	if(lBuildType && std::string(lBuildType) == "Debug") {
		std::ofstream out("epochPlot.txt");
		dump(out);
		out.close();
	}

	configureSeriesAxis();

	configureAxisLegend();

	configureParamsLegend(startTime, stopTime, intervalIndex);

	// configure X epoch axis
	_epochDecoratorPtr->configure(this, getEpochAxis(),
			getEpochAxis()->getRange().getMin(), getEpochAxis()->getRange().getMax(), _pParameterValues);

	getEpochAxis()->_used = true;

	PanelPlotOutput::preparePlotArea(startTime,stopTime,intervalIndex);
}

void EpochPlot::draw(double startTime, double stopTime, int intervalIndex,
		bool isFirstInterval, bool isLastInterval) {

	PanelPlotOutput::draw(startTime,stopTime,intervalIndex,isFirstInterval,isLastInterval);
}


void EpochPlot::calculatePlotArea(Bounds& bounds_) {
	PanelPlotOutput::calculatePlotArea(bounds_);
	// decorator is responsible of reserving extra space for what it manage (labels for instance).
	_epochDecoratorPtr->updatePlotArea(this, getEpochAxis(), bounds_);
}


void EpochPlot::configureSeriesAxis() {
	/*
	 * Configure the epoch time axis
	 */
	EpochAxis* epochAxis = getEpochAxis();

	if (epochAxis == NULL)
	{
		std::stringstream lError;
		lError << "EpochPlot::configureSeriesAxis: Cannot find epochAxis";
		BOOST_THROW_EXCEPTION(PanelPlotOutputException() << AMDA::ex_msg(lError.str()));
	}

	//get epoch axis range
	Range lEpochRange;
	if (!epochAxis->isNormalized())
	{
		//axis not normalized
		TimeIntervalList::iterator crtTimeInterval = _timeIntervalListPtr->begin();
		while (crtTimeInterval != _timeIntervalListPtr->end())
		{
			//get center time data for this interval
			std::vector<std::string>& crtCenterTimeData = _parameterManager.getInputIntervalDataList(crtTimeInterval->_index, _centerTimeId);
			if (crtCenterTimeData.empty())
			{
				std::stringstream lError;
				lError << "EpochPlot::configureSeriesAxis: Cannot get center time data for interval with index: " << crtTimeInterval->_index;
				BOOST_THROW_EXCEPTION(PanelPlotOutputException() << AMDA::ex_msg(lError.str()));
			}

			//compute left and right delta times (relative to the center time)
			double crtCenterTime  = AMDA::TimeUtil::readTimeInIso((char*)crtCenterTimeData[0].c_str());
			double leftDeltaTime  = crtCenterTime-crtTimeInterval->_startTime;
			double rightDeltaTime = crtTimeInterval->_stopTime-crtCenterTime;
			if ((leftDeltaTime < 0) || (rightDeltaTime < 0))
			{
				LOG4CXX_WARN(gLogger, "EpochPlot::configureSeriesAxis - centerTime not contained in the interval for the interval with index : " << crtTimeInterval->_index << " => skip this interval");
				++crtTimeInterval;
				continue;
			}

			//configure the range of the axis
			if (isnan(lEpochRange.getMin()) || (lEpochRange.getMin() > -leftDeltaTime))
				lEpochRange.setMin(-leftDeltaTime);

			if (isnan(lEpochRange.getMax()) || (lEpochRange.getMax() < rightDeltaTime))
				lEpochRange.setMax(rightDeltaTime);

			++crtTimeInterval;
		}
	}
	else
	{
		//normalized epoch axis
		lEpochRange.setMin(-1);
		lEpochRange.setMax(1);
	}

	if (isnan(lEpochRange.getMin()) || isnan(lEpochRange.getMax()))
	{
		std::stringstream lError;
		lError << "EpochPlot::configureSeriesAxis: Cannot configure the range of the epochAxis";
		BOOST_THROW_EXCEPTION(PanelPlotOutputException() << AMDA::ex_msg(lError.str()));
	}

	epochAxis->setRange(lEpochRange);

	/*
	 * configure y and z axis
	 */
	std::map<std::string, Range> lAxisRange;
	Range lColorAxeRange;
	SeriesProperties lSeriesProperties;

	boost::shared_ptr<ColorAxis> lZAxis = _panel->getColorAxis();

	// Parse each parameter to define on which axis to draw series.
	for (auto param: _parameterAxesList)
	{
		// Get number of series to draw
		// For each index of parameter identify to which axis series must be drawn.
		for(auto index: param.getYSerieIndexList(_pParameterValues))
		{
			lSeriesProperties = param.getYSeriePropertiesAt(index);
			if(!lSeriesProperties.hasYAxis()){
				continue;
			}
			boost::shared_ptr<Axis> lYAxis = _panel->getAxis(lSeriesProperties.getYAxisId());
			if (lYAxis.get() == nullptr) {
				std::stringstream lError;
				lError << "EpochPlot::configureSeriesAxis" << ": Y axis with id '" << lSeriesProperties.getYAxisId() << "' not found.";
				BOOST_THROW_EXCEPTION(PanelPlotOutputException() << AMDA::ex_msg(lError.str()));
			}
			lYAxis->_used = true;

			Range lRange(lYAxis->getRange());
			// If range status for this axis is set by the user do not update range "automatically".
			if (isnan(lRange.getMin()) && isnan(lRange.getMax()))
			{
				Range lEstimatedRange(lAxisRange[lYAxis->_id]);
				Range lParamIndexRange(
						(*_pParameterValues)[lSeriesProperties.getParamId()].getMin(index),
						(*_pParameterValues)[lSeriesProperties.getParamId()].getMax(index));

				if (isnan(lEstimatedRange.getMin()) && isnan(lEstimatedRange.getMax())) {
					lEstimatedRange.setMin(lParamIndexRange.getMin());
					lEstimatedRange.setMax(lParamIndexRange.getMax());
				} else {
					lEstimatedRange.setMin(std::min(lEstimatedRange.getMin(), lParamIndexRange.getMin()));
					lEstimatedRange.setMax(std::max(lEstimatedRange.getMax(), lParamIndexRange.getMax()));
				}
				lEstimatedRange._extend = lRange._extend;
				lAxisRange[lYAxis->_id] = lEstimatedRange;
			}

			// Set ZAxis range if a color param is defined for this serie
			if (lZAxis != nullptr)
			{
				if (!lSeriesProperties.getColorParamId().empty())
				{
					lZAxis->_used = true;
					Range lRange(lZAxis->getRange());
					ParameterAxes* colorSerieParameterAxes = getParameterAxesByColorSerieId(lSeriesProperties.getColorSerieId());
					if (colorSerieParameterAxes == NULL)
						continue;
					ColorSeriesProperties& colorSerieProp = colorSerieParameterAxes->getColorSeriePropertiesById(lSeriesProperties.getColorSerieId());
					// If range status for this axis is set by the user do not update range "automatically".
					if (isnan(lRange.getMin()) && isnan(lRange.getMax()))
					{
						Range lEstimatedRange(lColorAxeRange);
						Range lParamIndexRange(
							(*_pParameterValues)[lSeriesProperties.getColorParamId()].getMin(
								colorSerieProp.getIndex()),
							(*_pParameterValues)[lSeriesProperties.getColorParamId()].getMax(
								colorSerieProp.getIndex()));

						if (isnan(lEstimatedRange.getMin()) && isnan(lEstimatedRange.getMax())) {
							lEstimatedRange.setMin(lParamIndexRange.getMin());
							lEstimatedRange.setMax(lParamIndexRange.getMax());
						} else {
							lEstimatedRange.setMin(std::min(lEstimatedRange.getMin(), lParamIndexRange.getMin()));
							lEstimatedRange.setMax(std::max(lEstimatedRange.getMax(), lParamIndexRange.getMax()));
						}
						lEstimatedRange._extend = lRange._extend;
						lColorAxeRange = lEstimatedRange;
					}
				}
			}
		}
	}


	// Update range of axis. Done after because, axis range may be processed in several pass (one for each series)
	for (auto lAxis: lAxisRange) {
		_panel->getAxis(lAxis.first)->setRange(lAxis.second);
	}

	if (lZAxis != nullptr && lColorAxeRange.isSet())
		lZAxis->setRange(lColorAxeRange);
}

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

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

void EpochPlot::drawXAxis(boost::shared_ptr<Axis> pXAxis, PlWindow& pPlWindow, Bounds& pPlotAreaSize, TickConf& pTickConf){
	LOG4CXX_DEBUG(gLogger, "Drawing X axis ");
	// draw main axis...
	PanelPlotOutput::drawXAxis(pXAxis,pPlWindow, pPlotAreaSize, pTickConf);
	// delegate to decorator any extra drawing stuff it needs to perform...
	_epochDecoratorPtr->draw(this,getEpochAxis(), _pls);
}

void EpochPlot::drawSeries(double startDate, double stopDate, int intervalIndex, std::string pParamId,
		SeriesProperties& pSeries,
		AMDA::Common::ParameterIndexComponent pParamIndex, ParameterAxes& param,
		bool moreThanOneSerieForAxis) {
	LOG4CXX_DEBUG(gLogger, "Drawing epoch serie for parameter "<<pParamId<<"["<<pParamIndex.getDim1Index()<<","<<pParamIndex.getDim2Index()<<"]");
	// This will configure window, draw axes (if needed) and legend of axes.
	PanelPlotOutput::drawSeries(startDate, stopDate, intervalIndex, pParamId, pSeries, pParamIndex, param, moreThanOneSerieForAxis);

	if(!pSeries.hasYAxis())
		return;

	// Y axis may be missing (tickplot for example)
	std::string	yAxisId = pSeries.getYAxisId();
	if (yAxisId.empty())
		return;

	EpochAxis* epochAxis = getEpochAxis();

	//get center time data for this interval
	std::vector<std::string>& crtCenterTimeData = _parameterManager.getInputIntervalDataList(intervalIndex, _centerTimeId);
	if (crtCenterTimeData.empty())
	{
		std::stringstream lError;
		lError << "EpochPlot::configureSeriesAxis: Cannot get center time data for interval with index: " << intervalIndex;
		BOOST_THROW_EXCEPTION(PanelPlotOutputException() << AMDA::ex_msg(lError.str()));
	}
	double crtCenterTime  = AMDA::TimeUtil::readTimeInIso((char*)crtCenterTimeData[0].c_str());

	//set the current center time to the epoch axis
	epochAxis->setCrtCenterTime(crtCenterTime);

	//get computed values
	double *computedValues	= NULL;
	double *timeValues      = NULL;
	int    nbValues;
	if (!getComputedValuesFromSerieAndInterval(startDate, stopDate, pSeries, pParamIndex,
			&computedValues, &timeValues, nbValues))
	{
		LOG4CXX_DEBUG(gLogger, "EpochPlot::drawSeries - Cannot get computed values for serie");
		return;
	}

	//compute epoch time value
	double *epochTimeValues = epochAxis->getComputedValues(
			timeValues,
			nbValues,
			startDate,
			stopDate);

	double *coloredComputedValues = NULL;
	double *coloredTimeValues     = NULL;

	//get colored value if needed
	if (!pSeries.getColorParamId().empty() && (_panel->getColorAxis() != nullptr))
	{
		int nbColoredValues;
		if (!getColoredComputedValuesFromSerieAndInterval(startDate, stopDate, pSeries,
				&coloredComputedValues, &coloredTimeValues, nbColoredValues))
		{
			LOG4CXX_DEBUG(gLogger, "EpochPlot::drawSeries - Cannot get computed values for colored parameter");
			return;
		}
	}

	PlWindow lPlWindow = PlWindow(getEpochAxis()->getRange().getMin(), getEpochAxis()->getRange().getMax(),
			_panel->getAxis(yAxisId)->getRange().getMin(), _panel->getAxis(yAxisId)->getRange().getMax());

	_pls->wind(std::get<0>(lPlWindow), std::get<1>(lPlWindow),
				std::get<2>(lPlWindow), std::get<3>(lPlWindow));

	//draw serie
	Color lineColor   = getSerieLineColor(pSeries, moreThanOneSerieForAxis);
	Color symbolColor = getSerieSymbolColor(pSeries, lineColor);

	drawSymbols(
		pSeries.getSymbolProperties().getType(),
		pSeries.getSymbolProperties().getSize(), 1.,
		symbolColor,
		nbValues, epochTimeValues, computedValues, coloredComputedValues);

	drawLines(
		pSeries.getLineProperties().getType(),
		pSeries.getLineProperties().getStyle(),
		pSeries.getLineProperties().getWidth(),
		lineColor,
		nbValues, epochTimeValues, computedValues, coloredComputedValues);

	addSerieToParamsLegend(pSeries,pParamIndex,param._originalParamId, lineColor,symbolColor,startDate,stopDate,intervalIndex);

	delete[] computedValues;
	delete[] epochTimeValues;
	if (coloredComputedValues != NULL)
		delete[] coloredComputedValues;
}

const std::string EpochPlot::getXAxisId() const {
	// epoch plot can manage only one x axis.
	// search for its id

	for (auto param : _parameterAxesList)
	{
		//search epoch axis in color serie if exist
		if (!param.getYSeriePropertiesMap().empty())
			return param.getYSeriePropertiesMap().begin()->second.getXAxisId();
	}

	//not found, TODO see if we should raise an exception...
	return "";
}

void EpochPlot::setCenterTimeId(std::string centerTimeId)
{
	_centerTimeId = centerTimeId;
}


} /* namespace plot */