/* * TickMarkDecorator.cc * * Created on: Dec 18, 2013 * Author: amdadev */ #include "TickMarkDecorator.hh" #include #include #include #include "log4cxx/logger.h" #include "plplot/plplot.h" #include "boost/exception/all.hpp" #include "PlotLogger.hh" #include "TimeAxis.hh" #include "Page.hh" #include "Time/TimePlot.hh" #include "TickPlot.hh" #include "PlPlotUtil.hh" #include "ParamMgr.hh" using boost::tuples::get; namespace plot { log4cxx::LoggerPtr TickMarkDecorator::_logger( log4cxx::Logger::getLogger("AMDA-Kernel.Plot.Time.TickMarkDecorator")); TickMarkDecorator::TickMarkDecorator (PanelPlotOutput* decoratorPlot) : TimeAxisDecorator(), _valuesFormat("% -4.4G"), _decoratorPlot(decoratorPlot) { _pTimeInfo = TimeInfoPtr(new TimeInfo()); } TickMarkDecorator::TickMarkDecorator(const TickMarkDecorator& ref_) : TimeAxisDecorator(ref_), _tickInfoList(ref_._tickInfoList), _valuesFormat(ref_._valuesFormat), _decoratorPlot( ref_._decoratorPlot) { _pTimeInfo = TimeInfoPtr(new TimeInfo(*(ref_._pTimeInfo))); } TickMarkDecorator& TickMarkDecorator::operator=(const TickMarkDecorator& ref_) { if (_pTimeInfo == nullptr) { _pTimeInfo = boost::shared_ptr( new TimeInfo(*(ref_._pTimeInfo))); } else { _pTimeInfo->_timeFormat = ref_._pTimeInfo->_timeFormat; _pTimeInfo->_timeTitle = ref_._pTimeInfo->_timeTitle; } _tickInfoList = ref_._tickInfoList; _valuesFormat = ref_._valuesFormat; _decoratorPlot = ref_._decoratorPlot; return *this; } TickMarkDecorator::~TickMarkDecorator() { } void TickMarkDecorator::updatePlotArea(PanelPlotOutput* pplot_, Axis* axis_, const Bounds& panelBounds_, Bounds& bounds_) { LOG4CXX_DEBUG(_logger, "TickMarkDecorator Updating plot area : "<getLegendFont(pplot_->_panel.get())); PlPlotUtil::setPlFont(legendFont); CharSize legendSize = PlPlotUtil::getCharacterSizeInPlPage(pplot_->_panel->_page); // update top and bottom space if (_decoratorPlot->isStandalone()) { bounds_._y += (bounds_._height + legendSize.second * 1.2) ; bounds_._height = legendSize.second * 1.2; } else { int tickLines = 0; for (TickParamInfoList::iterator it = _tickInfoList.begin(); it != _tickInfoList.end(); ++it) { tickLines += it->_indexesInfo.size(); bool isScalar = (it->_indexesInfo.size() == 1) && (it->_indexesInfo[0]._index.getDim1Index() == -1) && (it->_indexesInfo[0]._index.getDim2Index() == -1); std::string description = getTickParamDescription(it->_paramId, isScalar); if (!description.empty()) { ++tickLines; } } double tickLinesHeight = tickLines * legendSize.second * 1.2; if (pAxis->_position == PlotCommon::POS_BOTTOM) { bounds_._y += tickLinesHeight; } bounds_._height -= tickLinesHeight; } double maxWidth = 0; for (TickParamInfoList::iterator it = _tickInfoList.begin(); it != _tickInfoList.end(); ++it) { bool isScalar = (it->_indexesInfo.size() == 1) && (it->_indexesInfo[0]._index.getDim1Index() == -1) && (it->_indexesInfo[0]._index.getDim2Index() == -1); std::string description = getTickParamDescription(it->_paramId, isScalar); if (!description.empty()) { double descWidth = PlPlotUtil::getTextWidthInPlPage(pplot_->_panel->_page, description.c_str()); maxWidth = std::max(maxWidth, descWidth); } for (std::vector::iterator itIndex = it->_indexesInfo.begin(); itIndex != it->_indexesInfo.end(); ++itIndex) { std::string label = getTickParamLabel(it->_paramId, (*itIndex)._index); double labelWidth = PlPlotUtil::getTextWidthInPlPage(pplot_->_panel->_page, label.c_str()); maxWidth = std::max(maxWidth, labelWidth); } } if (_pTimeInfo->_pAxis->_showTickMark) { double width = PlPlotUtil::getTextWidthInPlPage(pplot_->_panel->_page, _pTimeInfo->_timeTitle.c_str()); maxWidth = std::max(maxWidth, width); } PLFLT lDefaultCharHeight, lCurrentCharHeight; plgchr(&lDefaultCharHeight, &lCurrentCharHeight); maxWidth += 6 * lCurrentCharHeight / (std::get < 0 > (pplot_->_panel->_page->getSizeInMm()) * (pplot_->_panel->_page->_dpi / 90.)); if (_decoratorPlot->isStandalone()) { bounds_._x += maxWidth; bounds_._width -= maxWidth; } else if (bounds_._x - panelBounds_._x < maxWidth) { bounds_._width -= (maxWidth - bounds_._x + panelBounds_._x); bounds_._x += (maxWidth - bounds_._x + panelBounds_._x); } LOG4CXX_DEBUG(_logger, "Updated plot area : "<(pAxis_->getRange().getMin()); tm * lTimeTm = gmtime(&lTime); char lTimeChr[80]; // Format date. size_t datelen = strftime(lTimeChr, 80, getPlStartTimeFormat(pAxis_->_timeFormat, pAxis_->getRange().getMin(), pAxis_->getRange().getMax()).c_str(), lTimeTm); PlPlotUtil::setPlFont(pplot_->_panel->getFont()); CharSize fontSize = PlPlotUtil::getCharacterSizeInPlPage(pplot_->_panel->_page); return fontSize.first * datelen; } void TickMarkDecorator::configure(PanelPlotOutput* pplot_, Axis* axis_, double start_, double stop_, std::map *pParameterValues) { LOG4CXX_DEBUG(_logger, "TickMarkDecorator::configure"); _pParameterValues = pParameterValues; // retrieve time axis TimeAxis* pTimeAxis = (TimeAxis*) axis_; // configure x axis range pTimeAxis->setRange(start_, stop_); // show xAxis or not pTimeAxis->setOnlyTickmarks(_decoratorPlot->isStandalone()); setPlFormat( getPlTimeFormat(pTimeAxis->_timeFormat, pTimeAxis->getRange().getMin(), pTimeAxis->getRange().getMax(), getMajorTickNumber(pTimeAxis, pTimeAxis->getRange().getMin(), pTimeAxis->getRange().getMax()))); // install label generator installLabelGenerator(pplot_, pTimeAxis); } void TickMarkDecorator::draw(PanelPlotOutput* pplot_, Axis* axis_, std::shared_ptr pls_) { // draw a virtual axis for each entry // take into account plot orientation TimeAxis* pAxis = (TimeAxis*) axis_; // tick configuration for this axis : double xtick = getMajorTickSpace(pAxis, pAxis->getRange().getMin(), pAxis->getRange().getMax()); int nxsub = getMinorTickNumber(pAxis, pAxis->getRange().getMin(), pAxis->getRange().getMax(), xtick); // set color for drawing axis : Color lInitialColor = changeColor(pls_, pAxis->_color, pplot_->_panel->_page->_mode); // Set tick (major and minor) length factor PLFLT lTickLenFact = PanelPlotOutput::DEFAULT_TICK_LENGTH_FACTOR; if (!isnan(pAxis->_tick._lengthFactor)) { lTickLenFact = pAxis->_tick._lengthFactor; } // 0 => keep tick height in millimeters. pls_->smaj(0, lTickLenFact); pls_->smin(0, lTickLenFact); // Set font to draw axes. Font legendFont(axis_->getLegendFont(pplot_->_panel.get())); PlPlotUtil::setPlFont(legendFont); CharSize legendCharSize = PlPlotUtil::getCharacterSizeInPlPage(pplot_->_panel->_page); double yInc = legendCharSize.second * 1.2; std::string options = ""; int start; int end; int dir; bool top = (pAxis->_position == PlotCommon::POS_TOP); if (top) { options = "mxo"; // options means : m=display above axe, x=do not plot ticks, o=user label function provided. start = _tickInfoList.size() - 1; end = -1; dir = -1; } else { options = "nxo";// options means : n=display below axe, x=do not plot ticks, o=user label function provided. start = 0; end = _tickInfoList.size(); dir = 1; } // Get plot area viewport PLFLT lXMin, lXMax, lYMin, lYMax; pls_->gvpd(lXMin, lXMax, lYMin, lYMax); // Get window PLFLT xwmin, xwmax, ywmin, ywmax; pls_->gvpw(xwmin, xwmax, ywmin, ywmax); double position = top ? lYMax - yInc : lYMin; for (int i = start; i != end; i += dir) { if ((i == start) && _pTimeInfo->_pAxis->_showTickMark) { //Draw time axis legend pls_->vpor(lXMin, lXMax, position, position + yInc); pls_->wind(xwmin, xwmax, 0, 1); pls_->mtex("lv", 4, top ? 2 : -1, 1, _pTimeInfo->_timeTitle.c_str()); position -= dir*yInc; } bool isScalar = (_tickInfoList[i]._indexesInfo.size() == 1) && (_tickInfoList[i]._indexesInfo[0]._index.getDim1Index() == -1) && (_tickInfoList[i]._indexesInfo[0]._index.getDim2Index() == -1); std::string description = getTickParamDescription(_tickInfoList[i]._paramId, isScalar); int startIndex = 0; int endIndex = _tickInfoList[i]._indexesInfo.size(); if (top) { startIndex = _tickInfoList[i]._indexesInfo.size()-1; endIndex = -1; } for (int j = startIndex; j != endIndex; j += dir) { if ((j == 0) && !description.empty()) { // Draw description pls_->vpor(lXMin, lXMax, top ? position + yInc : position, top ? position + 2*yInc : position + yInc); pls_->wind(xwmin, xwmax, 0, 1); pls_->mtex("lv", 4, top ? 2 : -1, 1, description.c_str()); if (!top) { position -= dir*yInc; } } pls_->vpor(lXMin, lXMax, position, position + yInc); pls_->wind(xwmin, xwmax, 0, 1); // install label generator specific for that virtual axis pls_->slabelfunc(generateTickSerieLabel, &_tickInfoList[i]._indexesInfo[j]); // draw virtual axis pls_->box(options.c_str(), xtick, nxsub, "", 0, 0); // draw legend std::string label = getTickParamLabel(_tickInfoList[i]._paramId, _tickInfoList[i]._indexesInfo[j]._index); pls_->mtex("lv", 4, top ? 2 : -1, 1, label.c_str()); // restore viewport pls_->vpor(lXMin, lXMax, lYMin, lYMax); // restore window pls_->wind(xwmin, xwmax, ywmin, ywmax); // increment offset position -= dir*yInc; if (top && (j == 0) && !description.empty()) { position -= dir*yInc; } } } // Restore initial color. restoreColor(pls_, lInitialColor, pplot_->_panel->_page->_mode); } void TickMarkDecorator::installLabelGenerator(PanelPlotOutput* /*pplot_*/, TimeAxis* timeAxis_) { LOG4CXX_TRACE(_logger, "installing LabelGenerator function"); // axis label : LOG4CXX_TRACE(_logger, "processing axis : "<< timeAxis_->_legend.getText()); // install time format _pTimeInfo->_timeFormat = getPlFormat(); _pTimeInfo->_timeTitle = timeAxis_->_legend.getText(); _pTimeInfo->_pAxis = timeAxis_; // init maximum value len with formatted time : if (TimePlot::qsasconfig == NULL) { configqsas(1. / 86400., 0., 0., 0x0, 1, 1970, 0, 1, 0, 0, 0., &TimePlot::qsasconfig); } // build all series names : _tickInfoList.clear(); for (auto p : _decoratorPlot->_parameterAxesList) { LOG4CXX_DEBUG(_logger, " inspecting parameter : " << p._originalParamId); for(auto lSeriesProperties : p.getYSeriePropertiesList()) { TickParamInfo* paramInfoPtr = NULL; for (TickParamInfoList::iterator it = _tickInfoList.begin(); it != _tickInfoList.end(); ++it) { if (it->_paramId.compare(lSeriesProperties.getParamId()) == 0) { paramInfoPtr = &(*it); break; } } if (paramInfoPtr == NULL) { TickParamInfo paramInfo; paramInfo._paramId = lSeriesProperties.getParamId(); _tickInfoList.push_back(paramInfo); paramInfoPtr = &(_tickInfoList[_tickInfoList.size()-1]); } for (auto index : lSeriesProperties.getIndexList(_pParameterValues)) { TickParamIndexInfo* paramIndexInfoPtr = NULL; for (std::vector::iterator indexIt = paramInfoPtr->_indexesInfo.begin(); indexIt != paramInfoPtr->_indexesInfo.end(); ++indexIt) { if (indexIt->_index == index) { paramIndexInfoPtr = &(*indexIt); } } if (paramIndexInfoPtr == NULL) { TickParamIndexInfo indexInfo; indexInfo._valuesFormat = getValuesFormat(); indexInfo._parameterData = &((*_pParameterValues)[lSeriesProperties.getParamId()]); indexInfo._index = AMDA::Common::ParameterIndexComponent(index.getDim1Index(),index.getDim2Index()); paramInfoPtr->_indexesInfo.push_back(indexInfo); } } } } // no legend for this axe since it is displayed at axis origin: timeAxis_->_legend.clearLabels(); // install label generator for main axe (the one that is displayed) timeAxis_->_labelGenerator->_data = _pTimeInfo.get(); timeAxis_->_labelGenerator->_function = generateTickTimeLabel; } std::string TickMarkDecorator::getTickParamLabel(const std::string& paramId, const AMDA::Common::ParameterIndexComponent& index) { AMDA::Parameters::ParameterSPtr param = _decoratorPlot->_parameterManager.getParameter(paramId); AMDA::Info::ParamMgr* piMgr = AMDA::Info::ParamMgr::getInstance(); AMDA::Info::ParamInfoSPtr paramInfo = piMgr->getParamInfoFromId(param->getInfoId()); std::ostringstream osstr; bool skipIndex = false; if (paramInfo != nullptr) { if (paramInfo->getComponents(index).empty() == false) { osstr << paramInfo->getComponents(index); skipIndex = true; } else { osstr << paramInfo->getShortName(); } } else { osstr << paramId; } if (!skipIndex) { if(index.getDim1Index() != -1){ osstr << "[" << index.getDim1Index() ; if (index.getDim2Index() != -1) osstr << "," << index.getDim2Index(); osstr << "]"; } else if (index.getDim2Index() != -1) { osstr << "[" << index.getDim2Index() << "]"; } } if ((paramInfo != nullptr) && (paramInfo->getUnits().empty() == false)) { osstr << ", " << paramInfo->getUnits(); } return osstr.str(); } std::string TickMarkDecorator::getTickParamDescription(const std::string& paramId, bool isScalar) { AMDA::Parameters::ParameterSPtr param = _decoratorPlot->_parameterManager.getParameter(paramId); AMDA::Info::ParamMgr* piMgr = AMDA::Info::ParamMgr::getInstance(); AMDA::Info::ParamInfoSPtr paramInfo = piMgr->getParamInfoFromId(param->getInfoId()); std::string description; if (paramInfo != nullptr) { description = paramInfo->getInstrumentId(); if (!description.empty()) { if (description.find('_') != std::string::npos) { description = description.substr(0, description.find('_')); } std::transform(description.begin(), description.end(), description.begin(), ::toupper); } } if (description.empty() && !isScalar) { if (paramInfo != nullptr) { description = paramInfo->getShortName(); } if (description.empty()) { description = paramId; } } if ((paramInfo != nullptr) && (paramInfo->getCoordinatesSystem().empty() == false)) { description += ", "; description += paramInfo->getCoordinatesSystem(); } return description; } /***************************** EXTERNAL METHODS ********************************/ void generateTickSerieLabel(PLINT axis, PLFLT value, char *label, PLINT length, PLPointer data) { if (axis == PL_X_AXIS) { if (data == NULL) { std::cout << "WARNING !!!! NO DATA FOUND FOR generateTickSerieLabel FUNCTION !!!" << std::endl << std::endl; LOG4CXX_WARN(gLogger, "WARNING !!!! NO DATA FOUND FOR generateTickSerieLabel FUNCTION !!"); return; } TickParamIndexInfo* pInfo = static_cast (data); // display formatted value std::ostringstream labels; //get nearest value double crtTime = (double) value; int crtIndex = pInfo->_parameterData->indexOf(crtTime); if (crtIndex == -1 && (crtTime - (pInfo->_parameterData->getTimes()[pInfo->_parameterData->getSize()-1]) < pInfo->_parameterData->getParamGapSize())) //Fix last value crtIndex = pInfo->_parameterData->getSize()-1; else if ((crtTime - (pInfo->_parameterData->getTimes()[crtIndex])) > pInfo->_parameterData->getParamGapSize()) //Be sure that we are not in a data gap crtIndex = -1; if (crtIndex == -1) { labels << "NaN" << std::endl; } else { labels << boost::format(pInfo->_valuesFormat) % (*pInfo->_parameterData->getValues(pInfo->_index, crtIndex)); } snprintf(label, length, "%s", labels.str().c_str()); } } void generateTickTimeLabel(PLINT axis, PLFLT value, char *label, PLINT length, PLPointer data) { if (axis == PL_X_AXIS) { if (data == NULL) { std::cout << "WARNING !!!! NO DATA FOUND FOR generateOrbitTimeLabel FUNCTION !!!" << std::endl << std::endl; LOG4CXX_WARN(gLogger, "WARNING !!!! NO DATA FOUND FOR generateOrbitTimeLabel FUNCTION !!"); return; } TimeInfo* pInfo = static_cast (data); if (!pInfo->_pAxis->_showTickMark) { label [0] = 0; return; } // display formatted time std::string timeFormatstr = pInfo->_timeFormat.c_str(); char *timeFormat = new char[timeFormatstr.length() + 1]; strcpy(timeFormat, timeFormatstr.c_str()); if (TimePlot::qsasconfig == NULL) { configqsas(1. / 86400., 0., 0., 0x0, 1, 1970, 0, 1, 0, 0, 0., &TimePlot::qsasconfig); } char formattedTime[40]; strfqsas(formattedTime, 40, timeFormat, (double) value, TimePlot::qsasconfig); delete[] timeFormat; snprintf(label, length, "%s", formattedTime); } } } /* namespace plot */