/* * TimePlot.cc * * Created on: 22 nov. 2013 * Author: CS */ #include "TimePlot.hh" #include "ParamsNode.hh" #include "AxesNode.hh" #include "PlotOutput.hh" #include "TimeUtil.hh" #include "Parameter.hh" #include "ParamInfo.hh" #include "ParamTable.hh" #include "ParamMgr.hh" #include "ShadesTools.hh" #include "AxisLegendManager.hh" #include #include "DefaultTimeAxisDecorator.hh" #include "TickMarkDecorator.hh" #include "PlotLogger.hh" #include "ParamMgr.hh" #include "TimeUtil.hh" #include "PlPlotUtil.hh" #include "Range.hh" #include using namespace AMDA::Parameters; using namespace AMDA::Info; namespace plot { QSASConfig* TimePlot::qsasconfig = NULL; TimePlot::TimePlot(AMDA::Parameters::ParameterManager& manager, boost::shared_ptr panel, TimeAxisDecorator* timeAxisDecorator, bool isStandalone) : PanelPlotOutput(manager, panel, isStandalone), _startDateDrawn(false) { setTimeAxisDecorator(std::shared_ptr(timeAxisDecorator)); } TimePlot::~TimePlot() { if (qsasconfig != NULL) { free(qsasconfig); qsasconfig = nullptr; } } TimeAxis* TimePlot::getTimeAxis(){ std::string lAxisId = getXAxisId(); boost::shared_ptr xAxis = _panel->getAxis(lAxisId); if (xAxis.get() == nullptr) { std::stringstream lError; lError << "TimePlot::apply" << ": time axis with id '" << lAxisId << "' not found."; BOOST_THROW_EXCEPTION(PanelPlotOutputException() << AMDA::ex_msg(lError.str())); } return dynamic_cast(xAxis.get()); } void TimePlot::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("timePlot.txt"); dump(out); out.close(); } // Configure Series to draw on axis by checking which color to use and // number of series to draw on a same axis. configureSeriesAxis(); configureAxisLegend(); configureSpectroAxis(); configureParamsLegend(startTime,stopTime,intervalIndex); // configure X axis getTimeAxisDecorator()->configure(this, getTimeAxis(), startTime, stopTime, _pParameterValues); getTimeAxis()->_used = true; _pls->timefmt(getTimeAxisDecorator()->getPlFormat().c_str()); PanelPlotOutput::preparePlotArea(startTime,stopTime,intervalIndex); } bool TimePlot::draw(double startTime, double stopTime, int intervalIndex, bool isFirstInterval, bool isLastInterval) { bool dataPloted = PanelPlotOutput::draw(startTime,stopTime,intervalIndex,isFirstInterval,isLastInterval); // Draw start date if (!_startDateDrawn /*&& getTimeAxis()->_used*/) drawStartDate (getTimeAxis(), startTime,stopTime); _startDateDrawn = true; return dataPloted; } void TimePlot::calculatePlotArea(const Bounds& panelBounds_, Bounds& bounds_) { PanelPlotOutput::calculatePlotArea(panelBounds_, bounds_); // decorator is responsible of reserving extra space for what it manage (labels for instance). getTimeAxisDecorator()->updatePlotArea(this, getTimeAxis(), panelBounds_, bounds_); } void TimePlot::configureSeriesAxis() { // map std::map lAxisRange; Range lColorAxeRange; SeriesProperties lSeriesProperties; boost::shared_ptr 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 lSeriesProperties: param.getYSeriePropertiesList()) { if(!lSeriesProperties.hasYAxis()){ continue; } boost::shared_ptr lYAxis = _panel->getAxis(lSeriesProperties.getYAxisId()); if (lYAxis.get() == nullptr) { std::stringstream lError; lError << "TimePlot::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]); for (auto index : lSeriesProperties.getIndexList(_pParameterValues)) { Range lParamIndexRange( (*_pParameterValues)[lSeriesProperties.getParamId()].getMin(index), (*_pParameterValues)[lSeriesProperties.getParamId()].getMax(index)); ErrorBarProperties & errBarProp = lSeriesProperties.getErrorBarProperties(); // Update lParamIndexRange depending on the use of ErrorBars on the plot if (errBarProp.getErrorMinMax() != nullptr) { Range lParamMinRange( (*_pParameterValues)[errBarProp.getErrorMinMax()->getUsedParamMin()].getMin(-1), (*_pParameterValues)[errBarProp.getErrorMinMax()->getUsedParamMin()].getMax(-1)); Range lParamMaxRange( (*_pParameterValues)[errBarProp.getErrorMinMax()->getUsedParamMax()].getMin(-1), (*_pParameterValues)[errBarProp.getErrorMinMax()->getUsedParamMax()].getMax(-1)); if (lParamMinRange.getMin() < lParamIndexRange.getMin()) lParamIndexRange.setMin(lParamMinRange.getMin()); if (lParamMinRange.getMax() > lParamIndexRange.getMax()) lParamIndexRange.setMax(lParamMinRange.getMax()); if (lParamMaxRange.getMin() < lParamIndexRange.getMin()) lParamIndexRange.setMin(lParamMaxRange.getMin()); if (lParamMaxRange.getMax() > lParamIndexRange.getMax()) lParamIndexRange.setMax(lParamMaxRange.getMax()); } 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())); } if(lParamIndexRange.getMin()==lParamIndexRange.getMax()) lEstimatedRange.setMargin(0.05); } lEstimatedRange._extend = lRange._extend; lAxisRange[lYAxis->_id] = lEstimatedRange; // la } // 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())); } if(lParamIndexRange.getMin()==lParamIndexRange.getMax()) lEstimatedRange.setMargin(0.05); 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) { boost::shared_ptr lYAxis = _panel->getAxis(lAxis.first); Range lRange(lAxis.second); fixRange(lRange, lYAxis-> _scale == Axis::Scale::LOGARITHMIC); lYAxis->setRange(lRange); } if (lZAxis != nullptr && lColorAxeRange.isSet()) { fixRange(lColorAxeRange, lZAxis->_scale == Axis::Scale::LOGARITHMIC); lZAxis->setRange(lColorAxeRange); } } void TimePlot::configureAxisLegend() { // Y axis AxisLegendManager::configureYAxisLegendForSpectro(this); AxisLegendManager::configureYAxisLegendForSeries(this); // Z axis AxisLegendManager::configureColorAxisLegendForSpectro(this); AxisLegendManager::configureColorAxisLegendForSeries(this); } void TimePlot::configureSpectroAxis() { // Parse each parameter to define on which axis to draw spectro for (auto param: _parameterAxesList) { std::shared_ptr spectroPropertiesPtr = param.getSpectroProperties(); if (spectroPropertiesPtr == nullptr) return; //no spectro defined if(!spectroPropertiesPtr->hasYAxis()) return; LOG4CXX_DEBUG(gLogger, "Spectro Y axis is " << spectroPropertiesPtr->getYAxisId()); boost::shared_ptr lYAxis = _panel->getAxis(spectroPropertiesPtr->getYAxisId()); if (lYAxis.get() == nullptr) { std::stringstream lError; lError << "TimePlot::configureSpectroAxis" << ": Y axis with id '" << spectroPropertiesPtr->getYAxisId() << "' not found."; BOOST_THROW_EXCEPTION(PanelPlotOutputException() << AMDA::ex_msg(lError.str())); } lYAxis->_used = true; //set Z axis range boost::shared_ptr lZAxis = _panel->getAxis(spectroPropertiesPtr->getZAxisId()); if (lZAxis.get() == nullptr) { std::stringstream lError; lError << "TimePlot::configureSpectroAxis" << ": Z axis with id '" << spectroPropertiesPtr->getZAxisId() << "' not found."; BOOST_THROW_EXCEPTION(PanelPlotOutputException() << AMDA::ex_msg(lError.str())); } lZAxis->_used = true; ParameterSPtr p = _parameterManager.getParameter(param._originalParamId); int parameterDimension; if (spectroPropertiesPtr->getRelatedDim() == 0) parameterDimension = (*_pParameterValues)[spectroPropertiesPtr->getParamId()].getDim1Size(); else parameterDimension = (*_pParameterValues)[spectroPropertiesPtr->getParamId()].getDim2Size(); //set Y axis range Range lYAxisRange = lYAxis->Axis::getRange(); AMDA::Info::ParamInfoSPtr paramInfo = AMDA::Info::ParamMgr::getInstance()->getParamInfoFromId(p->getInfoId()); if (isnan(lYAxisRange.getMin()) && isnan(lYAxisRange.getMax())) { boost::shared_ptr tableSPtr; if (paramInfo != nullptr) tableSPtr = paramInfo->getTable(spectroPropertiesPtr->getRelatedDim()); if (tableSPtr == nullptr) { LOG4CXX_DEBUG(gLogger, "No table defined => use index"); lYAxisRange.setMin(std::min(0.,lYAxisRange.getMin())); lYAxisRange.setMax(std::max((double)parameterDimension,lYAxisRange.getMax())); } else { AMDA::Info::t_TableBound crtBound; if (!tableSPtr->isVariable(&_parameterManager)) { for (int i = 0; i < parameterDimension; ++i) { crtBound = tableSPtr->getBound(&_parameterManager, i); if (!std::isnan(crtBound.min)) { if (!((lYAxis->_scale == Axis::Scale::LOGARITHMIC) && (crtBound.min <= 0))) lYAxisRange.setMin(std::min(crtBound.min,lYAxisRange.getMin())); } if (!std::isnan(crtBound.max)) { if (!((lYAxis->_scale == Axis::Scale::LOGARITHMIC) && (crtBound.max <= 0))) lYAxisRange.setMax(std::max(crtBound.max,lYAxisRange.getMax())); } } } else { //Variable table => we need to loop under all records to find axis min & max values for (int i = 0; i < (*_pParameterValues)[spectroPropertiesPtr->getParamId()].getSize(); ++i) { std::map> paramsTableData; for (std::map::iterator it = spectroPropertiesPtr->getTableParams().begin(); it != spectroPropertiesPtr->getTableParams().end(); ++it) { ParameterData& data = (*_pParameterValues)[it->second]; std::vector paramTableValues; for (int j = 0; j < data.getDim1Size(); ++j) { if (i < data.getSize()) { double* values = data.getValues(AMDA::Common::ParameterIndexComponent(j), i); paramTableValues.push_back((*values)); } } paramsTableData[it->first] = paramTableValues; } for (int j = 0; j < parameterDimension; ++j) { crtBound = tableSPtr->getBound(&_parameterManager, j, ¶msTableData); if (!std::isnan(crtBound.min)) { if (!((lYAxis->_scale == Axis::Scale::LOGARITHMIC) && (crtBound.min <= 0))) lYAxisRange.setMin(std::min(crtBound.min,lYAxisRange.getMin())); } if (!std::isnan(crtBound.max)) { if (!((lYAxis->_scale == Axis::Scale::LOGARITHMIC) && (crtBound.max <= 0))) lYAxisRange.setMax(std::max(crtBound.max,lYAxisRange.getMax())); } } } } } } //do not extend the axis lYAxisRange._extend = false; fixRange(lYAxisRange, lYAxis->_scale == Axis::Scale::LOGARITHMIC); lYAxis->setRange(lYAxisRange); LOG4CXX_DEBUG(gLogger, "Y axis range : min = " << lYAxisRange.getMin() << ", max = " << lYAxisRange.getMax()); Range lParamRange = lZAxis->Axis::getRange(); if (isnan(lParamRange.getMin()) && isnan(lParamRange.getMax())) { //auto range for(auto index : spectroPropertiesPtr->getIndexes()) { //compute global range for all indexes double minVal, maxVal; if (lZAxis->_scale == Axis::Scale::LOGARITHMIC) minVal = (*_pParameterValues)[spectroPropertiesPtr->getParamId()].getMinStrictPos(index); else minVal = (*_pParameterValues)[spectroPropertiesPtr->getParamId()].getMin(index); maxVal = (*_pParameterValues)[spectroPropertiesPtr->getParamId()].getMax(index); if (!isnan(minVal)) lParamRange.setMin(std::min(minVal,lParamRange.getMin())); //else // lParamRange.setMin(0); if (!isnan(maxVal)) lParamRange.setMax(std::max(maxVal,lParamRange.getMax())); //else // lParamRange.setMax(10); } if (isnan(lParamRange.getMin())) lParamRange.setMin(0); if (isnan(lParamRange.getMax())) lParamRange.setMax(10); } else lParamRange._extend = false; //set z axis range fixRange(lParamRange, lZAxis->_scale == Axis::Scale::LOGARITHMIC); LOG4CXX_DEBUG(gLogger, "ZAxis range : ZMin = " << lParamRange.getMin() << ", ZMax = " << lParamRange.getMax()); lZAxis->setRange(lParamRange); } } /** * @brief Identify if other side of the plot area need to be drawn or not. * @note A plot area side need to be drawn when there is no axis associated to it. */ std::string TimePlot::drawOppositeSide(boost::shared_ptr pAxis){ if( pAxis.get() == getTimeAxis() && getTimeAxis()->isOnlyTickmarks()){ return ""; } else{ return PanelPlotOutput::drawOppositeSide(pAxis); } } void TimePlot::drawXAxis(boost::shared_ptr 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... getTimeAxisDecorator()->draw(this,getTimeAxis(), _pls); } // Convert an X axis string value to a double X value assuming it's a DD_Time value double TimePlot::convertXAxisValue(const std::string &value) { return DD_Time2Double (value.c_str()); } ConstantLine * TimePlot::getConstantLineFromId (int serieId, int constantId, boost::shared_ptr& yAxis) { for (auto parameter : _parameterAxesList) { for (auto serieProperties : parameter.getYSeriePropertiesList()) { if ((serieProperties.getId() == serieId) &&(serieProperties.hasYAxis() == true)) { yAxis = _panel->getAxis(serieProperties.getYAxisId()); for (auto constantLine : yAxis->_constantLines) { if (constantLine->getId() == constantId) { return constantLine.get(); } } } } } // ConstantId not found, we throw an exception std::stringstream lError; lError << "TimePlot::getConstantLineFromId : Unable to find constantId='" << constantId << "' for serieId='" << serieId << "'"; BOOST_THROW_EXCEPTION(PanelPlotOutputException() << AMDA::ex_msg(lError.str())); return NULL; } bool TimePlot::getSeriePropertiesById(int serieId, SeriesProperties &rSerieProperties) { for (auto parameter : _parameterAxesList) { for (auto serieProperties : parameter.getYSeriePropertiesList()) { if (serieProperties.getId() == serieId) { rSerieProperties = serieProperties; return true; } } } return false; } void TimePlot::mergeAndSortTime ( double *values1Time, int values1Nb, double *values2Time, int values2Nb, std::vector &valuesTime) { // Build 2 vectors std::vector v1(values1Time, values1Time + values1Nb); std::vector v2(values2Time, values2Time + values2Nb); // Populate resulting vector valuesTime.insert( valuesTime.end(), v1.begin(), v1.end() ); valuesTime.insert( valuesTime.end(), v2.begin(), v2.end() ); // Sort and remove duplicates for the resulting vector sort (valuesTime.begin(), valuesTime.end()); valuesTime.erase( unique( valuesTime.begin(), valuesTime.end() ), valuesTime.end() ); } double TimePlot::getInterpolatedValue (double *values, double *valuesTime, int valuesNb, double atTime) { // get index of first time after given time_ if(isnan(atTime)){ return nan(""); } double *pTime = valuesTime; for(int t=0; t= atTime){ if (t == 0) { return nan(""); } else { double v1 = values [t-1]; double v2 = values [t]; double d1 = valuesTime [t-1]; double d2 = valuesTime [t]; // Return a linear interpolation of the value for the index return (v1 + (v2-v1) * (atTime-d1)/(d2-d1)); } } pTime++; } return nan(""); } bool TimePlot::intersect ( double xi, double y1i, double y2i, double xj, double y1j, double y2j, double *xInter) { // Simply checks if segment are above or below the other if ( ((y1i >= y2i) && (y1j >= y2j)) ||( (y1i <= y2i) && (y1j <= y2j)) ) { return false; } // Computes intersection point : // Compute a and b (y = a * x + b) for each segment double a1 = (y1j - y1i) / (xj - xi); double b1 = y1i - a1 * xi ; double a2 = (y2j - y2i) / (xj - xi); double b2 = y2i - a2 * xi ; // Compute intersection point (a1*x+b1 = a2*x+b2) *xInter = (b2-b1) / (a1-a2); return true; } void TimePlot::addIntersectionTime (double *values1, double *values1Time, int values1Nb, double *values2, double *values2Time, int values2Nb, std::vector &valuesTime) { if (valuesTime.empty() == true) return; // For each time segment compute intersection if it exists std::vector intersectionTime; double xi = valuesTime [0]; double y1i = getInterpolatedValue (values1, values1Time, values1Nb, xi); double y2i = getInterpolatedValue (values2, values2Time, values2Nb, xi); for (size_t t=1; t &valuesTime, bool colorGreaterSpecified, Color& colorGreater, bool colorLessSpecified, Color& colorLess, SeriesProperties &rSeriesProperties) { // Get X and Y axis. boost::shared_ptr lXAxis(_panel->getAxis(rSeriesProperties.getXAxisId())); boost::shared_ptr lYAxis(_panel->getAxis(rSeriesProperties.getYAxisId())); Range lXRange = getXAxisRange (rSeriesProperties, lXAxis); Range lYRange = getYAxisRange (rSeriesProperties, lYAxis); _pls->wind(lXRange.getMin(), lXRange.getMax(), lYRange.getMin(), lYRange.getMax()); PLFLT x[4], y[4], deltai, deltaj; Color curColor; Color lInitialColor = changeColor(_pls, colorGreater, _panel->_page->_mode); for ( size_t i = 0; i < (valuesTime.size() - 1); i++ ) { x[0] = valuesTime[i]; x[1] = x[0]; x[2] = valuesTime[i+1]; x[3] = x[2]; y[0] = getInterpolatedValue (values1, values1Time, values1Nb, valuesTime[i]); y[1] = getInterpolatedValue (values2, values2Time, values2Nb, valuesTime[i]); y[2] = getInterpolatedValue (values2, values2Time, values2Nb, valuesTime[i+1]); y[3] = getInterpolatedValue (values1, values1Time, values1Nb, valuesTime[i+1]); deltai = y[1] - y[0]; deltaj = y[2] - y[3]; // Set fill color depending on segment position if ( ((deltai > 0) && (fabs(deltai) > fabs(deltaj))) || ((deltaj > 0) && (fabs(deltaj) > fabs(deltai))) ) { // s1 above s2 if (colorGreaterSpecified == true) { curColor = colorGreater; } else { continue; } } else if ( ((deltai < 0) && (fabs(deltai) > fabs(deltaj))) || ((deltaj < 0) && (fabs(deltaj) > fabs(deltai))) ) { // s1 under s2 if (colorLessSpecified == true) { curColor = colorLess; } else { continue; } } else { // Colinear segments, no fill required continue; } // Fill polygon using the given color changeColor(_pls, curColor, _panel->_page->_mode); _pls->fill(4, x, y); } // Restore color. restoreColor(_pls, lInitialColor, _panel->_page->_mode); } void TimePlot::drawFills(double startDate, double stopDate) { PanelPlotOutput::drawFills(startDate, stopDate); LOG4CXX_DEBUG(gLogger, "TimePlot::drawFills"); SeriesProperties rSeriesProperties; double *values1 = NULL; double *values1Time = NULL; int values1Nb = 0; double *values2 = NULL; double *values2Time = NULL; int values2Nb = 0; std::vector valuesTime; // Drawing Fill Area located between Serie and Constant (horizontal) line for (auto fillSerieConstant : _panel->_fillSerieConstants) { // Retrieve serie parameter values for serieId if (!getSeriePropertiesById(fillSerieConstant->getSerieId(), rSeriesProperties)) { LOG4CXX_DEBUG(gLogger, "TimePlot::drawFills - Cannot find serie id " << fillSerieConstant->getSerieId()); continue; } //get computed values for this serie and interval if (!getComputedValuesFromSerieAndInterval(startDate, stopDate, rSeriesProperties, rSeriesProperties.getIndex(), &values1, &values1Time, values1Nb)) { LOG4CXX_DEBUG(gLogger, "TimePlot::drawFills - Cannot get computed values for serie id " << fillSerieConstant->getSerieId()); continue; } // Retrieve constantLine informations for these fill boost::shared_ptr yAxis; ConstantLine *constantLine = getConstantLineFromId ( fillSerieConstant->getSerieId(), fillSerieConstant->getConstantId(), yAxis); // Retrieve axis attachment // Build values2 values2Time array with 2 values : yConst & (first time, last time) for serie values2 = new double [2]; values2Time = new double [2]; values2 [0] = convertYAxisValue (constantLine->getValue()); values2 [1] = convertYAxisValue (constantLine->getValue()); if (yAxis->_scale == Axis::Scale::LOGARITHMIC) { values2[0] = log10 (values2[0]); values2[1] = log10 (values2[1]); } values2Time [0] = values1Time [0]; values2Time [1] = values1Time [values1Nb-1]; values2Nb = 2; // Build time vector by merging existing times and computing intersections, and draw it ! mergeAndSortTime (values1Time, values1Nb, values2Time, values2Nb, valuesTime); addIntersectionTime (values1, values1Time, values1Nb, values2, values2Time, values2Nb, valuesTime); drawFillArea (values1, values1Time, values1Nb, values2, values2Time, values2Nb, valuesTime, fillSerieConstant->isColorGreaterSpecified(), fillSerieConstant->getColorGreater(), fillSerieConstant->isColorLessSpecified(), fillSerieConstant->getColorLess(), rSeriesProperties); delete [] values1; delete [] values2; delete [] values2Time; } // Drawing Fill Area located between first and second Serie for (auto fillSerieSerie : _panel->_fillSerieSeries) { // Retrieve serie parameter values for firstSerieId if (!getSeriePropertiesById(fillSerieSerie->getFirstSerieId(), rSeriesProperties)) { LOG4CXX_DEBUG(gLogger, "TimePlot::drawFills - Cannot find serie id " << fillSerieSerie->getFirstSerieId()); continue; } //get computed values for this serie and interval if (!getComputedValuesFromSerieAndInterval(startDate, stopDate, rSeriesProperties, rSeriesProperties.getIndex(), &values1, &values1Time, values1Nb)) { LOG4CXX_DEBUG(gLogger, "TimePlot::drawFills - Cannot get computed values for serie id " << fillSerieSerie->getFirstSerieId()); continue; } // Retrieve serie parameter values for secondSerieId if (!getSeriePropertiesById(fillSerieSerie->getSecondSerieId(), rSeriesProperties)) { LOG4CXX_DEBUG(gLogger, "TimePlot::drawFills - Cannot find serie id " << fillSerieSerie->getSecondSerieId()); continue; } //get computed values for this serie and interval if (!getComputedValuesFromSerieAndInterval(startDate, stopDate, rSeriesProperties, rSeriesProperties.getIndex(), &values2, &values2Time, values2Nb)) { LOG4CXX_DEBUG(gLogger, "TimePlot::drawFills - Cannot get computed values for serie id " << fillSerieSerie->getSecondSerieId()); continue; } // Build time vector by merging existing times and computing intersections, and draw it ! mergeAndSortTime (values1Time, values1Nb, values2Time, values2Nb, valuesTime); addIntersectionTime (values1, values1Time, values1Nb, values2, values2Time, values2Nb, valuesTime); drawFillArea (values1, values1Time, values1Nb, values2, values2Time, values2Nb, valuesTime, fillSerieSerie->isColorGreaterSpecified(), fillSerieSerie->getColorGreater(), fillSerieSerie->isColorLessSpecified(), fillSerieSerie->getColorLess(), rSeriesProperties); delete [] values1; delete [] values2; } } void TimePlot::drawSeries(double startDate, double stopDate, int intervalIndex, std::string pParamId, SeriesProperties& pSeries, AMDA::Common::ParameterIndexComponent pParamIndex, ParameterAxes& param, bool moreThanOneSerieForAxis) { LOG4CXX_DEBUG(gLogger, "TimePlot::drawSeries - Drawing serie for parameter "<getColorAxis() != nullptr)) { int nbColoredValues; if (!getColoredComputedValuesFromSerieAndInterval(startDate, stopDate, pSeries, &coloredComputedValues, &coloredTimeValues, nbColoredValues)) { LOG4CXX_DEBUG(gLogger, "TimePlot::drawSeries - Cannot get computed values for colored parameter"); return; } } PlWindow lPlWindow = PlWindow(getTimeAxis()->getRange().getMin(), getTimeAxis()->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, timeValues, computedValues, coloredComputedValues); drawLines( pSeries.getLineProperties().getType(), pSeries.getLineProperties().getStyle(), pSeries.getLineProperties().getWidth(), lineColor, nbValues, timeValues, computedValues, coloredComputedValues); //add serie to param legend addSerieToParamsLegend(pSeries,pParamIndex,param._originalParamId, lineColor,symbolColor,startDate,stopDate,intervalIndex); //draw interval drawSerieInterval(pSeries,timeValues,computedValues,timeValues,nbValues,intervalIndex); delete[] computedValues; if (coloredComputedValues != NULL) delete[] coloredComputedValues; // Draw min/max error bars if required ErrorBarProperties &errorBarProp = pSeries.getErrorBarProperties(); if (errorBarProp.getErrorMinMax() != nullptr) { //get computed min/max error data double *minComputedValues = NULL; double *minTimeValues = NULL; int nbMinValues; double *maxComputedValues = NULL; double *maxTimeValues = NULL; int nbMaxValues; if (!getErrorComputedValuesFromSerieAndInterval(startDate, stopDate, pSeries, &minComputedValues, &minTimeValues, nbMinValues, &maxComputedValues, &maxTimeValues, nbMaxValues)) { LOG4CXX_DEBUG(gLogger, "TimePlot::drawSeries - Cannot get min/max error values for serie"); return; } LineProperties &lineProps = pSeries.getErrorBarProperties().getLineProperties(); drawYErrors( lineProps.getType(), lineProps.getStyle(), lineProps.getWidth(), lineProps.getColor(), nbMinValues, minTimeValues, minComputedValues, maxComputedValues); // Free values delete [] minComputedValues; delete [] maxComputedValues; } } void TimePlot::drawSpectro(double startDate, double stopDate, std::string pParamId, SpectroProperties& pSpectro) { LOG4CXX_DEBUG(gLogger, "TimePlot::drawSpectro Drawing spectro for parameter "< tableSPtr; AMDA::Info::ParamInfoSPtr paramInfo = AMDA::Info::ParamMgr::getInstance()->getParamInfoFromId(p->getInfoId()); if (paramInfo != nullptr) tableSPtr = paramInfo->getTable(pSpectro.getRelatedDim()); //get axis boost::shared_ptr lXAxis = _panel->getAxis(pSpectro.getXAxisId()); boost::shared_ptr lYAxis = _panel->getAxis(pSpectro.getYAxisId()); boost::shared_ptr lZAxis = _panel->getColorAxis(); //Check dimensions if (pSpectro.getRelatedDim() == 0) { if (data.getDim1Size() + 1 <= 0) { LOG4CXX_INFO(gLogger, "TimePlot::drawSpectro - No data to plot"); return; } } else { if (data.getDim2Size() + 1 <= 0) { LOG4CXX_INFO(gLogger, "TimePlot::drawSpectro - No data to plot"); return; } } LOG4CXX_DEBUG(gLogger, "TimePlot::drawSpectro - Build data grid"); MatrixGrid matrixGrid; if ((tableSPtr == nullptr) || !tableSPtr->isVariable(&_parameterManager)) { int startIndex; int nbValues; data.getIntervalBounds(startDate, stopDate, startIndex, nbValues); for (auto index : pSpectro.getIndexes()) { GridPart part; if (tableSPtr == nullptr) { if (pSpectro.getRelatedDim() == 0) part.y[0] = index.getDim1Index(); else part.y[0] = index.getDim2Index(); part.y[1] = part.y[0]+1; } else { AMDA::Info::t_TableBound crtBound; if (pSpectro.getRelatedDim() == 0) crtBound = tableSPtr->getBound(&_parameterManager, index.getDim1Index()); else crtBound = tableSPtr->getBound(&_parameterManager, index.getDim2Index()); if (std::isnan(crtBound.min) || std::isnan(crtBound.max)) continue; part.y[0] = crtBound.min; part.y[1] = crtBound.max; if (lYAxis->_scale == Axis::Scale::LOGARITHMIC) { if ((crtBound.min <= 0) || (crtBound.max <= 0)) continue; part.y[0] = log10(part.y[0]); part.y[1] = log10(part.y[1]); } } for (int i = 0; i < nbValues - 1; ++i) { part.x[0] = data.getTimes()[startIndex+i]; part.x[1] = data.getTimes()[startIndex+i+1]; part.isColorIndex = false; part.value = data.getValues(index, startIndex)[i]; matrixGrid.push_back(part); } } } else { //Variable table AMDA::Info::t_TableBound crtBound; //for (auto index : pSpectro.getIndexes()) //{ int startIndex; int nbValues; data.getIntervalBounds(startDate, stopDate, startIndex, nbValues); for (int i = 0; i < nbValues - 1; ++i) { GridPart part; part.x[0] = data.getTimes()[startIndex+i]; part.x[1] = data.getTimes()[startIndex+i+1]; std::map> paramsTableData; for (std::map::iterator it = pSpectro.getTableParams().begin(); it != pSpectro.getTableParams().end(); ++it) { ParameterData& tableData = (*_pParameterValues)[it->second]; std::vector paramTableValues; for (int j = 0; j < tableData.getDim1Size(); ++j) { double* values = tableData.getValues(AMDA::Common::ParameterIndexComponent(j,-1), i); paramTableValues.push_back((*values)); } paramsTableData[it->first] = paramTableValues; } for (auto index : pSpectro.getIndexes()) { part.isColorIndex = false; part.value = data.getValues(index,startIndex)[i]; if (pSpectro.getRelatedDim() == 0) crtBound = tableSPtr->getBound(&_parameterManager, index.getDim1Index(), ¶msTableData); else crtBound = tableSPtr->getBound(&_parameterManager, index.getDim2Index(), ¶msTableData); part.y[0] = crtBound.min; part.y[1] = crtBound.max; if (!std::isnan(crtBound.min) && !std::isnan(crtBound.max)) { if (lYAxis->_scale == Axis::Scale::LOGARITHMIC) { if ((crtBound.min > 0) && (crtBound.max > 0)) { part.y[0] = log10(part.y[0]); part.y[1] = log10(part.y[1]); matrixGrid.push_back(part); } } else matrixGrid.push_back(part); } } } } //get specific colors for min / max values Color minValColor = lZAxis->getMinValColor(); Color maxValColor = lZAxis->getMaxValColor(); //draw spectro LOG4CXX_DEBUG(gLogger, "TimePlot::drawSpectro - Draw data grid"); drawMatrix(matrixGrid, pSpectro.getMin(), pSpectro.getMax(), minValColor, maxValColor, lZAxis->_color._colorMapIndex, pSpectro.getUseLog0AsMin()); } Color TimePlot::getStatusColor(AMDA::Info::ParamInfoSPtr& paramInfo, double value) { Color color; if ((paramInfo != nullptr) && !paramInfo->getStatusDef().empty()) { for (int s = 0 ; s < (int)paramInfo->getStatusDef().size(); ++s) { if (value >= paramInfo->getStatusDef()[s].getMinValue() && value <= paramInfo->getStatusDef()[s].getMaxValue()) { if (!paramInfo->getStatusDef()[s].getColor().empty()) { std::string colorStr = paramInfo->getStatusDef()[s].getColor(); createColor(color, colorStr); return color; } } } } return color; } void TimePlot::drawOneInterval(double tmin, double tmax, Color color) { if (!color.isSet()) { return; } //Fill interval PanelPlotOutput::drawRectangle(tmin, tmax, 0., 1., color, 0.25); //Draw borders PLFLT x[2], y[2]; x[0] = tmin; y[0] = 0.; x[1] = tmin; y[1] = 1.; PanelPlotOutput::drawLines(LineType::LINE, LineStyle::PLAIN, 2, color, 2, x, y); x[0] = tmax; y[0] = 0.; x[1] = tmax; y[1] = 1.; PanelPlotOutput::drawLines(LineType::LINE, LineStyle::PLAIN, 2, color, 2, x, y); } void TimePlot::drawIntervals(double startDate, double stopDate, std::string pParamId, IntervalsProperties& pIntervals) { LOG4CXX_DEBUG(gLogger, "TimePlot::drawIntervals Drawing intervals for parameter "<getRange().getMin(), getTimeAxis()->getRange().getMax(), 0, 1); _pls->wind(std::get<0>(lPlWindow), std::get<1>(lPlWindow), std::get<2>(lPlWindow), std::get<3>(lPlWindow)); ParameterData& data = (*_pParameterValues)[pIntervals.getParamId()]; ParameterSPtr p = _parameterManager.getParameter(pIntervals.getParamId()); AMDA::Info::ParamInfoSPtr paramInfo = AMDA::Info::ParamMgr::getInstance()->getParamInfoFromId(p->getInfoId()); //get computed values int startIndex, nbData; data.getIntervalBounds(startDate, stopDate, startIndex, nbData); Color crtColor; // Draw intervals double crtVal = NAN; double tmin = 0, tmax = 0; for (int i = 0; i < nbData - 1; ++i) { double t1 = data.getTimes()[startIndex+i]; double t2 = data.getTimes()[startIndex+i+1]; double v = *data.getValues(AMDA::Common::ParameterIndexComponent(-1,-1), startIndex+i); if (isNAN(crtVal)) { tmin = t1; tmax = t2; crtVal = v; continue; } else if (crtVal == v) { tmax = t2; continue; } crtColor = getStatusColor(paramInfo, crtVal); drawOneInterval(tmin, tmax, crtColor); tmin = t1; tmax = t2; crtVal = v; crtColor = Color(); } //Draw last interval if need if ((tmin != tmax) && !isNAN(crtVal)) { crtColor = getStatusColor(paramInfo, crtVal); drawOneInterval(tmin, tmax, crtColor); } } /** * @brief Draw further information (for instance start date). */ void TimePlot::drawStartDate(TimeAxis* pXAxis, double startTime, double stopTime) { LOG4CXX_DEBUG(gLogger, "Drawing start date."); if (pXAxis->_showTickMark == false || !pXAxis->_drawn || !pXAxis->_visible) return; // use panel font to draw start date. // If font of legend is not defined, use panel font. Font font(_panel->getFont()); // PlPlotUtil::setPlFont(font); //CharSize charsize = PlPlotUtil::getCharacterSizeInPlPage(_panel->_page); // panel bounds. Bounds lPanelBounds(_panel->getBoundsInPlPage()); // plotting area bounds (i.e. current viewport dimensions) PLFLT lXMin, lXMax, lYMin, lYMax; _pls->gvpd(lXMin, lXMax, lYMin, lYMax); // position of reference point of string along the bottom edge // expressed as a fraction of the length of the bottom edge // set it at the very beginning of edge and use justification to handle // reverse axis. float lPosition = 0.0; // display it one line above the main panel bottom border : float disp = -1; long int lTime = static_cast(startTime); tm * lTimeTm = gmtime(&lTime); char lTimeChr[80]; // Format date. strftime(lTimeChr, 80, getPlStartTimeFormat(pXAxis->_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); // Draw start date. if (pXAxis->_reverse) { lPosition = 1.; if (dateWidthInMm*x_subpage_per_mm > lPanelBounds._x + lPanelBounds._width - lXMax) lPosition = 1. - (dateWidthInMm*x_subpage_per_mm - (lPanelBounds._x + lPanelBounds._width - lXMax)) / (lXMax - lXMin); _pls->mtex("b", disp, lPosition , 0., lTimeChr); } else { 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); } const std::string TimePlot::getXAxisId() const { // time plot can manage only one x axis. // search for its id for (auto param : _parameterAxesList) { //search time axis in color serie if exist if (!param.getYSeriePropertiesList().empty()) return param.getYSeriePropertiesList().begin()->getXAxisId(); //search time axis in spectro if exist if (param.getSpectroProperties() != nullptr) return param.getSpectroProperties()->getXAxisId(); } return DefaultPlotConfiguration::TIME_DEFAULT_ID; } } /* namespace plot */