/* * TimePlot.cc * * Created on: 22 nov. 2013 * Author: CS */ #include "TimePlot.hh" #include "AxesNode.hh" #include "AxisLegendManager.hh" #include "DefaultTimeAxisDecorator.hh" #include "ParamInfo.hh" #include "ParamMgr.hh" #include "ParamTable.hh" #include "Parameter.hh" #include "ParamsNode.hh" #include "PlPlotUtil.hh" #include "PlotLogger.hh" #include "PlotOutput.hh" #include "Range.hh" #include "ShadesTools.hh" #include "TickMarkDecorator.hh" #include "TimeUtil.hh" #include #include #include #include #include #include #include #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(); configureSauvaudAxis(); configureIntervalsLegend(); 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 lYAxisRequestRange = lYAxis->Axis::getRequestedRange(); Range lRange = lYAxis->Axis::getRange(); // If range status for this axis is set by the user do not update range "automatically". if (isnan(lYAxisRequestRange.getMin()) && isnan(lYAxisRequestRange.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; } // 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::configureIntervalsLegend() { ParamMgr *piMgr = ParamMgr::getInstance(); for (auto param : _parameterAxesList) { std::shared_ptr intervalsPropertiesPtr = param.getIntervalsProperties(); ParameterSPtr p = _parameterManager.getParameter(param._originalParamId); AMDA::Info::ParamInfoSPtr paramInfo = piMgr->getParamInfoFromId(p->getInfoId()); if (intervalsPropertiesPtr != nullptr) { boost::shared_ptr pTextLegendProperties = intervalsPropertiesPtr->getLegend(); if (pTextLegendProperties->getText() == "") { pTextLegendProperties->setText(paramInfo->getShortName()); } pTextLegendProperties->setColor(intervalsPropertiesPtr->getColor()); } } } 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) continue; //no spectro defined if (!spectroPropertiesPtr->hasYAxis()) continue; 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 lYAxisRequestRange = lYAxis->Axis::getRequestedRange(); Range lYAxisRange = lYAxis->Axis::getRange(); AMDA::Info::ParamInfoSPtr paramInfo = AMDA::Info::ParamMgr::getInstance()->getParamInfoFromId(p->getInfoId()); if (isnan(lYAxisRequestRange.getMin()) && isnan(lYAxisRequestRange.getMax())) { boost::shared_ptr tableSPtr; if (paramInfo != nullptr) tableSPtr = paramInfo->getTable(spectroPropertiesPtr->getRelatedDim()); if (tableSPtr == nullptr) { // look for unique embedded table boost::shared_ptr linkedTableSPtr; int counter = 0; for (auto pInfo : paramInfo->getLinkedParamList()) { linkedTableSPtr = AMDA::Info::ParamMgr::getInstance()->getParamInfoFromId(pInfo)->getTable(spectroPropertiesPtr->getRelatedDim()); if (linkedTableSPtr == nullptr) continue; counter++; } if (linkedTableSPtr == nullptr || counter != 1) { LOG4CXX_DEBUG(gLogger, "table cannot be defined from linked info params"); } else { tableSPtr = linkedTableSPtr; } } 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())) { std::string normalization = spectroPropertiesPtr->getNormalization(); if (normalization == "log" || normalization == "linear") { lParamRange.setMin(0); lParamRange.setMax(1); } else { //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); } } void TimePlot::configureSauvaudAxis() { // Parse each parameter to define on which axis to draw Sauvaud for (auto param : _parameterAxesList) { std::shared_ptr sauvaudPropertiesPtr = param.getSauvaudProperties(); if (sauvaudPropertiesPtr == nullptr) continue; //no sauvaud defined if (!sauvaudPropertiesPtr->hasYAxis()) continue; LOG4CXX_DEBUG(gLogger, "Sauvaud Y axis is " << sauvaudPropertiesPtr->getYAxisId()); boost::shared_ptr lYAxis = _panel->getAxis(sauvaudPropertiesPtr->getYAxisId()); if (lYAxis.get() == nullptr) { std::stringstream lError; lError << "TimePlot::configureSauvaudAxis" << ": Y axis with id '" << sauvaudPropertiesPtr->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(sauvaudPropertiesPtr->getZAxisId()); if (lZAxis.get() == nullptr) { std::stringstream lError; lError << "TimePlot::configureSauvaudAxis" << ": Z axis with id '" << sauvaudPropertiesPtr->getZAxisId() << "' not found."; BOOST_THROW_EXCEPTION(PanelPlotOutputException() << AMDA::ex_msg(lError.str())); } lZAxis->_used = true; ParameterSPtr p = _parameterManager.getParameter(param._originalParamId); int rightParameterDimension; int rightDim = sauvaudPropertiesPtr->getRightDim(); if (rightDim == 0){ rightParameterDimension = (*_pParameterValues)[sauvaudPropertiesPtr->getParamId()].getDim1Size(); }else{ rightParameterDimension = (*_pParameterValues)[sauvaudPropertiesPtr->getParamId()].getDim2Size(); } //set Y axis range AMDA::Info::ParamInfoSPtr paramInfo = AMDA::Info::ParamMgr::getInstance()->getParamInfoFromId(p->getInfoId()); // configure right axis Range lYAxisRequestRange = lYAxis->Axis::getRequestedRange(); Range lYAxisRange = lYAxis->Axis::getRange(); if (isnan(lYAxisRequestRange.getMin()) && isnan(lYAxisRequestRange.getMax())) { boost::shared_ptr rightTableSPtr; if (paramInfo != nullptr) { rightTableSPtr = paramInfo->getTable(rightDim); } if (rightTableSPtr == nullptr) { LOG4CXX_DEBUG(gLogger, "No table defined => use index"); lYAxisRange.setMin(std::min(0., lYAxisRange.getMin())); lYAxisRange.setMax(std::max((double)rightParameterDimension, lYAxisRange.getMax())); } else { AMDA::Info::t_TableBound crtBound; if (!rightTableSPtr->isVariable(&_parameterManager)) { for (int i = 0; i < rightParameterDimension; ++i) { crtBound = rightTableSPtr->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)[sauvaudPropertiesPtr->getParamId()].getSize(); ++i) { std::map> paramsTableData; for (std::map::iterator it = sauvaudPropertiesPtr->getTableParams().begin(); it != sauvaudPropertiesPtr->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 < rightParameterDimension; ++j) { crtBound = rightTableSPtr->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 right axis range : min = " << lYAxisRange.getMin() << ", max = " << lYAxisRange.getMax()); Range lParamRange = lZAxis->Axis::getRange(); if (isnan(lParamRange.getMin()) && isnan(lParamRange.getMax())) { std::string normalization = sauvaudPropertiesPtr->getNormalization(); if (normalization == "log" || normalization == "linear") { lParamRange.setMin(0); lParamRange.setMax(1); } else { // auto range for (auto index : sauvaudPropertiesPtr->getIndexes()) { // compute global range for all indexes double minVal, maxVal; if (lZAxis->_scale == Axis::Scale::LOGARITHMIC) minVal = (*_pParameterValues)[sauvaudPropertiesPtr->getParamId()].getMinStrictPos(index); else minVal = (*_pParameterValues)[sauvaudPropertiesPtr->getParamId()].getMin(index); maxVal = (*_pParameterValues)[sauvaudPropertiesPtr->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 < valuesNb; t++) { // No interpolation required if (*pTime == atTime) { return values[t]; } // Interpolation required if (*pTime >= 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.size(); t++) { double xj = valuesTime[t]; double y1j = getInterpolatedValue(values1, values1Time, values1Nb, xj); double y2j = getInterpolatedValue(values2, values2Time, values2Nb, xj); double xInter; if (intersect(xi, y1i, y2i, xj, y1j, y2j, &xInter) == true) { intersectionTime.push_back(xInter); } // Next vector element xi = xj; y1i = y1j; y2i = y2j; } // Add intersections informations to the resulting values valuesTime.insert(valuesTime.end(), intersectionTime.begin(), intersectionTime.end()); // Sort and remove duplicates for the resulting vector sort(valuesTime.begin(), valuesTime.end()); valuesTime.erase(unique(valuesTime.begin(), valuesTime.end()), valuesTime.end()); } void TimePlot::drawFillArea(double *values1, double *values1Time, int values1Nb, double *values2, double *values2Time, int values2Nb, std::vector &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 ¶m, bool moreThanOneSerieForAxis) { LOG4CXX_DEBUG(gLogger, "TimePlot::drawSeries - Drawing 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; //get computed values double *computedValues = NULL; double *timeValues = NULL; int nbValues; if (!getComputedValuesFromSerieAndInterval(startDate, stopDate, pSeries, pParamIndex, &computedValues, &timeValues, nbValues)) { LOG4CXX_DEBUG(gLogger, "TimePlot::drawSeries - Cannot get computed values for serie"); return; } 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, "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 " << pParamId); // This will configure window, draw axes (if needed) and legend of axes. PanelPlotOutput::drawSpectro(startDate, stopDate, pParamId, pSpectro); //get parameter data and info ParameterSPtr p = _parameterManager.getParameter(pParamId); ParameterData &data = (*_pParameterValues)[pSpectro.getParamId()]; boost::shared_ptr tableSPtr; AMDA::Info::ParamInfoSPtr paramInfo = AMDA::Info::ParamMgr::getInstance()->getParamInfoFromId(p->getInfoId()); if (paramInfo != nullptr) tableSPtr = paramInfo->getTable(pSpectro.getRelatedDim()); if (tableSPtr == nullptr){ boost::shared_ptr linkedTableSPtr; int counter = 0; // work on this for (auto pInfo :paramInfo->getLinkedParamList()){ linkedTableSPtr = AMDA::Info::ParamMgr::getInstance()->getParamInfoFromId(pInfo)->getTable(pSpectro.getRelatedDim()); if (linkedTableSPtr == nullptr) continue; counter ++; } if(linkedTableSPtr == nullptr || counter !=1){ LOG4CXX_DEBUG(gLogger, "table cannot be defined from linked info params"); }else{ tableSPtr = linkedTableSPtr; } } //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; //get specific colors for min / max values Color minValColor = lZAxis->getMinValColor(); Color maxValColor = lZAxis->getMaxValColor(); if ((tableSPtr == nullptr) || !tableSPtr->isVariable(&_parameterManager)) { int startIndex; // index corresponding to startDate 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]); } } if (lYAxis->_reverse) { if (((part.y[0] < lYAxis->getRange().getMax()) && (part.y[1] < lYAxis->getRange().getMax())) || ((part.y[0] > lYAxis->getRange().getMin()) && (part.y[1] < lYAxis->getRange().getMin()))) { continue; } } else { if (((part.y[0] < lYAxis->getRange().getMin()) && (part.y[1] < lYAxis->getRange().getMin())) || ((part.y[0] > lYAxis->getRange().getMax()) && (part.y[1] < lYAxis->getRange().getMax()))) { continue; } } 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); } //Apply background subtraction applyBackgroundSubtraction(matrixGrid, pSpectro, data); //draw spectro LOG4CXX_DEBUG(gLogger, "TimePlot::drawSpectro - Draw data grid - " << matrixGrid.size()); std::string normalization = pSpectro.getNormalization(); if (!normalization.empty()) normalize(matrixGrid, normalization); drawMatrix(matrixGrid, pSpectro.getMin(), pSpectro.getMax(), minValColor, maxValColor, lZAxis->_color._colorMapIndex, pSpectro.getUseLog0AsMin()); matrixGrid.clear(); } } 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); } } //draw spectro LOG4CXX_DEBUG(gLogger, "TimePlot::drawSpectro - Draw data grid - " << matrixGrid.size()); std::string normalization = pSpectro.getNormalization(); if (!normalization.empty()) normalize(matrixGrid, normalization); drawMatrix(matrixGrid, pSpectro.getMin(), pSpectro.getMax(), minValColor, maxValColor, lZAxis->_color._colorMapIndex, pSpectro.getUseLog0AsMin()); matrixGrid.clear(); } } } void TimePlot::drawSauvaud(double startDate, double stopDate, std::string pParamId, SauvaudProperties &pSauvaud, int /*subIndex=0*/, int /*subsNumber=1*/, std::string /*opositeLegend*/) { LOG4CXX_DEBUG(gLogger, "TimePlot::drawSauvaud Drawing sauvaud for parameter " << pParamId); //get parameter data and info ParameterSPtr p = _parameterManager.getParameter(pParamId); ParameterData &data = (*_pParameterValues)[pSauvaud.getParamId()]; int rightDim = pSauvaud.getRightDim(); int leftDim = (rightDim ==0) ? 1 :0; int rightDimSize =-1; int leftDimSize =-1; bool invertedTable = false; if(rightDim ==0){ rightDimSize = data.getDim1Size(); leftDimSize = data.getDim2Size() ; }else{ rightDimSize = data.getDim2Size() ; leftDimSize = data.getDim1Size() ; } if ( rightDimSize < 0 && leftDimSize<0 ) { LOG4CXX_INFO(gLogger, "TimePlot::drawSauvaud - No data to plot"); return; } boost::shared_ptr rightTableSPtr; boost::shared_ptr leftTableSPtr; AMDA::Info::ParamInfoSPtr paramInfo = AMDA::Info::ParamMgr::getInstance()->getParamInfoFromId(p->getInfoId()); if (paramInfo != nullptr){ rightTableSPtr = paramInfo->getTable(rightDim); leftTableSPtr = paramInfo->getTable(leftDim); invertedTable = leftTableSPtr->isInvertedOrder(&_parameterManager); } //get axis boost::shared_ptr lXAxis = _panel->getAxis(pSauvaud.getXAxisId()); boost::shared_ptr lYAxis = _panel->getAxis(pSauvaud.getYAxisId()); boost::shared_ptr lZAxis = _panel->getColorAxis(); // Init data int startIndex; // index corresponding to startDate int nbValues; data.getIntervalBounds(startDate, stopDate, startIndex, nbValues); LOG4CXX_DEBUG(gLogger, "TimePlot::drawSauvaud - Build data grid"); int plotIndex; for(int spectroIndex =0; spectroIndex < leftDimSize; spectroIndex ++){ // This will configure window, draw axes (if needed) and legend of axes. plotIndex = (!invertedTable)? spectroIndex : leftDimSize-spectroIndex-1; // set left label for each sub-plot label table value or index std::string plotLabel = std::to_string(spectroIndex); if ((leftTableSPtr == nullptr) || !leftTableSPtr->isVariable(&_parameterManager)) { if ( leftTableSPtr== nullptr) { // nothing to do }else{ AMDA::Info::t_TableBound rightBound = leftTableSPtr->getBound(&_parameterManager, spectroIndex); if (!std::isnan(rightBound.min) && !std::isnan(rightBound.max)){ double boundVal = (rightBound.min + rightBound.max)/2; plotLabel = std::to_string((int)std::round(boundVal)); } } }else{ // variable for (int i = 0; i < nbValues - 1; ++i){ std::map> paramsTableData; for (std::map::iterator it = pSauvaud.getTableParams().begin(); it != pSauvaud.getTableParams().end(); ++it) { ParameterData &tableData = (*_pParameterValues)[it->second]; std::vector paramTableValues; for (int j = 0; j < rightDimSize; ++j) { double *values = tableData.getValues(AMDA::Common::ParameterIndexComponent(j, -1), i); paramTableValues.push_back((*values)); } paramsTableData[it->first] = paramTableValues; } AMDA::Info::t_TableBound rightBound = leftTableSPtr->getBound(&_parameterManager, spectroIndex, ¶msTableData); if (!std::isnan(rightBound.min) && !std::isnan(rightBound.max)){ double boundVal = (rightBound.min + rightBound.max)/2; plotLabel = std::to_string((int)std::round(boundVal)); } } } Label label(lYAxis->_legend.getFont(), lYAxis->_legend.getColor()); label._text = plotLabel; lYAxis->_legend.setLabel(label); std::string opositeLabel =""; if(spectroIndex == leftDimSize -1){ opositeLabel = " "+rightTableSPtr ->getName(&_parameterManager); opositeLabel = opositeLabel + "\n ["+rightTableSPtr ->getUnits(&_parameterManager)+"]"; PanelPlotOutput::drawSauvaud(startDate, stopDate, pParamId, pSauvaud,plotIndex , leftDimSize, opositeLabel); }else{ PanelPlotOutput::drawSauvaud(startDate, stopDate, pParamId, pSauvaud,plotIndex , leftDimSize, ""); } MatrixGrid matrixGrid; //get specific colors for min / max values Color minValColor = lZAxis->getMinValColor(); Color maxValColor = lZAxis->getMaxValColor(); if ((rightTableSPtr == nullptr) || !rightTableSPtr->isVariable(&_parameterManager)) { AMDA::Common::ParameterIndexComponentList indexes = pSauvaud.getIndexesByIndex(spectroIndex, leftDim); for (auto index : indexes) { GridPart part; if ( rightTableSPtr== nullptr) { if (rightDim == 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 (rightDim == 0) crtBound = rightTableSPtr->getBound(&_parameterManager, index.getDim1Index()); else crtBound = rightTableSPtr->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]); } } if (lYAxis->_reverse) { if (((part.y[0] < lYAxis->getRange().getMax()) && (part.y[1] < lYAxis->getRange().getMax())) || ((part.y[0] > lYAxis->getRange().getMin()) && (part.y[1] < lYAxis->getRange().getMin()))) { continue; } } else { if (((part.y[0] < lYAxis->getRange().getMin()) && (part.y[1] < lYAxis->getRange().getMin())) || ((part.y[0] > lYAxis->getRange().getMax()) && (part.y[1] < lYAxis->getRange().getMax()))) { continue; } } 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); } //Apply background subtraction applyBackgroundSubtraction(matrixGrid, pSauvaud, data); //draw sauvaud LOG4CXX_DEBUG(gLogger, "TimePlot::drawSauvaud - Draw data grid - " << matrixGrid.size()); std::string normalization = pSauvaud.getNormalization(); if (!normalization.empty()) normalize(matrixGrid, normalization); drawMatrix(matrixGrid, pSauvaud.getMin(), pSauvaud.getMax(), minValColor, maxValColor, lZAxis->_color._colorMapIndex, pSauvaud.getUseLog0AsMin()); matrixGrid.clear(); } }else{ //Variable table AMDA::Info::t_TableBound crtBound; 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 = pSauvaud.getTableParams().begin(); it != pSauvaud.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 : pSauvaud.getIndexesByIndex(spectroIndex, leftDim)) { part.isColorIndex = false; part.value = data.getValues(index, startIndex)[i]; if (rightDim == 0) crtBound = rightTableSPtr->getBound(&_parameterManager, index.getDim1Index(), ¶msTableData); else crtBound = rightTableSPtr->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); } //draw sauvaud LOG4CXX_DEBUG(gLogger, "TimePlot::drawSauvaud - Draw data grid - " << matrixGrid.size()); std::string normalization = pSauvaud.getNormalization(); if (!normalization.empty()) normalize(matrixGrid, normalization); drawMatrix(matrixGrid, pSauvaud.getMin(), pSauvaud.getMax(), minValColor, maxValColor, lZAxis->_color._colorMapIndex, pSauvaud.getUseLog0AsMin()); matrixGrid.clear(); } } } } // draw legend std::string plotLabel = " "+leftTableSPtr ->getName(&_parameterManager); plotLabel = plotLabel + "\n ["+leftTableSPtr ->getUnits(&_parameterManager)+"]"; Label label(lYAxis->_legend.getFont(), lYAxis->_legend.getColor()); label._text = plotLabel; lYAxis->_legend.setLabel(label); PanelPlotOutput::drawSauvaud(startDate, stopDate, pParamId, pSauvaud, leftDimSize, leftDimSize, ""); } void TimePlot::applyBackgroundSubtraction(MatrixGrid &matrixGrid, SpectroProperties &pSpectro, ParameterData &data) { //We get the background subtraction value given by the user const double backgroundSubValue = pSpectro.getBackgroundSubValue(); //If negative value then quit and do nothing if (backgroundSubValue < 0) return; const int len = matrixGrid.size(); //Channel subtraction if (pSpectro.getBackgroundSubType() == SpectroProperties::BackgroundSubType::BYCHANNEL) { int indexChannelDim1 = -1; int indexChannelDim2 = -1; //The selected dimension of the background subtraction if (pSpectro.getBackgroundSubDim() == 0) indexChannelDim1 = (int)backgroundSubValue; else indexChannelDim2 = (int)backgroundSubValue; //Get the values which corresponds to the selected channel double *values = data.getValues(AMDA::Common::ParameterIndexComponent(indexChannelDim1, indexChannelDim2), 0); //Apply the subtraction for (int i = 0; i < len; i++) matrixGrid[i].value -= values[i]; } //Fixed value substraction else if (pSpectro.getBackgroundSubType() == SpectroProperties::BackgroundSubType::FIXEDVALUE) { for (int i = 0; i < len; i++) matrixGrid[i].value -= backgroundSubValue; } else { std::stringstream lError; lError << "Unknown background substracion type " << pSpectro.getBackgroundSubType(); BOOST_THROW_EXCEPTION(PanelPlotOutputException() << AMDA::ex_msg(lError.str())); } return; } void TimePlot::normalize(MatrixGrid &matrixGrid, std::string normalizationType) { if (normalizationType == "none") return; double min = DBL_MAX; double max = DBL_MIN; int len = matrixGrid.size(); if (normalizationType == "log") { for (auto val : matrixGrid) { if (val.value < min && val.value > 0) min = val.value; if (val.value > max) max = val.value; } } else { for (auto val : matrixGrid) { if (val.value < min) min = val.value; if (val.value > max) max = val.value; } } if (std::isnan(min) || std::isnan(max)) { return; } else if (min == max) { for (int i = 0; i < len; i++) matrixGrid[i].value = matrixGrid[i].value / max; } else { if (normalizationType == "log") { for (int i = 0; i < len; i++) matrixGrid[i].value = (std::log10(matrixGrid[i].value) - std::log10(min)) / (std::log10(max) - std::log10(min)); return; } else if (normalizationType == "linear") { for (int i = 0; i < len; i++) matrixGrid[i].value = (matrixGrid[i].value - min) / (max - min); return; } else { std::stringstream lError; lError << "Unknwon normalization " << normalizationType; BOOST_THROW_EXCEPTION(PanelPlotOutputException() << AMDA::ex_msg(lError.str())); } } } Color TimePlot::getStatusColor(AMDA::Info::ParamInfoSPtr ¶mInfo, 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 " << pParamId); // This will configure window, draw axes (if needed) and legend of axes. PanelPlotOutput::drawIntervals(startDate, stopDate, pParamId, pIntervals); PlWindow lPlWindow = PlWindow(getTimeAxis()->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 = 0; 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 = pIntervals.getColor(); drawOneInterval(tmin, tmax, crtColor); tmin = t1; tmax = t2; crtVal = v; crtColor = Color(); } //Draw last interval if need if ((tmin != tmax) && !isNAN(crtVal)) { crtColor = pIntervals.getColor(); 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 */