/* * 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" using boost::tuples::get; namespace plot { log4cxx::LoggerPtr TickMarkDecorator::_logger( log4cxx::Logger::getLogger("AMDA-Kernel.Plot.Time.TickMarkDecorator")); TickMarkDecorator::TickMarkDecorator (bool isStandalone, PanelPlotOutput* decoratorPlot) : TimeAxisDecorator(), _seriesInfoList(), _valuesFormat("% -4.4G"), _maxValueLen(0), _isStandalone(isStandalone), _decoratorPlot(decoratorPlot) { _pTimeInfo = TimeInfoPtr(new TimeInfo()); } TickMarkDecorator::TickMarkDecorator(const TickMarkDecorator& ref_) : TimeAxisDecorator(ref_), _seriesInfoList( ref_._seriesInfoList), _valuesFormat(ref_._valuesFormat), _maxValueLen( ref_._maxValueLen), _isStandalone(ref_._isStandalone), _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; _pTimeInfo->_isFirstLabel = ref_._pTimeInfo->_isFirstLabel; _pTimeInfo->_maxLen = ref_._pTimeInfo->_maxLen; } _seriesInfoList = ref_._seriesInfoList; _valuesFormat = ref_._valuesFormat; _maxValueLen = ref_._maxValueLen; _isStandalone = ref_._isStandalone; _decoratorPlot = ref_._decoratorPlot; return *this; } TickMarkDecorator::~TickMarkDecorator() { } void TickMarkDecorator::updatePlotArea(PanelPlotOutput* pplot_, Axis* axis_, Bounds& bounds_) { LOG4CXX_DEBUG(_logger, "TickMarkDecorator Updating plot area : "<getMargin(); // calculate char size for labels: Font legendFont(pAxis->_legend.getFont()); Font panelFont(pplot_->_panel->getFont()); if (!legendFont.isSet()) { legendFont = panelFont; } PlPlotUtil::setPlFont(legendFont); CharSize legendSize = PlPlotUtil::getCharacterSizeInPlPage(pplot_->_panel->_page); double tickmarkSpace = 0; if (pAxis->_visible) { // process room for label tickmarks : double lineHeight = legendSize.second * 2; // take into account first space between time label tickmarkSpace += lineHeight * 1.5; // reserve space for all series labels : tickmarkSpace += (lineHeight * _seriesInfoList.size()); } else { LOG4CXX_DEBUG(_logger, " Axis not visible nothing to do."); } // update y and height; x and width are left unchanged. // origin of viewport is at left,bottom corner. // to take into account the tickmarks. bounds_._height -= tickmarkSpace; // specific case when axis labels are displayed at bottom of the graph. Needs to update y. if (pAxis->_position == PlotCommon::POS_BOTTOM) { bounds_._y += tickmarkSpace; } // needs to reserve enough space for start date above PlPlotUtil::setPlFont(pplot_->_panel->getFont()); CharSize startDateSize = PlPlotUtil::getCharacterSizeInPlPage(pplot_->_panel->_page); bounds_._height -= startDateSize.second * 3; bounds_._y += startDateSize.second * 3; // take margins into account : // compute what we need for labels at left side : // reserve same space amount in both side (since text are centered on ticks, only half length is taken into account). double minspaceleft = std::max((const int) (_pTimeInfo->_maxLen + 1), (const int) _maxValueLen + 1); if (legendFont.getSize() > panelFont.getSize()) { minspaceleft *= legendSize.first; } else { PlPlotUtil::setPlFont(panelFont); CharSize panelSize = PlPlotUtil::getCharacterSizeInPlPage(pplot_->_panel->_page); minspaceleft *= panelSize.first; } minspaceleft *= 0.5; double minspaceright = minspaceleft; // we also need to reserve enough space for start date information // (only in width, because height is already taken into account in PlotPanelOutput). if (pAxis->_reverse) { minspaceright = std::max(minspaceright, computeStartDateWidth(pplot_, pAxis)); } else { minspaceleft = std::max(minspaceleft, computeStartDateWidth(pplot_, pAxis)); } // LOG4CXX_DEBUG(_logger,"min space="<(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(_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_; // Get plot area viewport. Bounds vpBounds; PLFLT lXMin, lXMax, lYMin, lYMax; pls_->gvpd(lXMin, lXMax, lYMin, lYMax); vpBounds._x = lXMin; vpBounds._y = lYMin; vpBounds._width = lXMax - lXMin; vpBounds._height = lYMax - lYMin; // get windows Bounds winBounds; PLFLT xwmin, xwmax, ywmin, ywmax; pls_->gvpw(xwmin, xwmax, ywmin, ywmax); winBounds._x = xwmin; winBounds._y = ywmin; winBounds._width = xwmax - xwmin; winBounds._height = ywmax - ywmin; LOG4CXX_DEBUG(_logger, " viewport :"<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. std::vector panelStyles; Font panelFont(pplot_->_panel->getFont()); Font legendFont(pAxis->_legend.getFont()); if (!legendFont.isSet()) { // font has not been defined for that axis, used default panel font : legendFont = panelFont; } // viewport translation only along y direction: we translate about n charHeight. So, gets panel character height. PlPlotUtil::setPlFont(panelFont); CharSize panelCharSize = PlPlotUtil::getCharacterSizeInPlPage(pplot_->_panel->_page); PlPlotUtil::setPlFont(legendFont); CharSize legendCharSize = PlPlotUtil::getCharacterSizeInPlPage(pplot_->_panel->_page); // delta is negative when axis is at bottom (which is the default case). positive otherwise. // (viewport origin is in the left bottom corner of the page). double yDelta = -legendCharSize.second * 2; std::string options = ""; int start; int end; int dir; PlPlotUtil::setPlFont(legendFont); if (pAxis->_position == PlotCommon::POS_TOP) { options = "mxo"; // options means : m=display above axe, x=do not plot ticks, o=user label function provided. yDelta = -yDelta; // if axis is displayed on top of graph we have to use an inverse translation delta. // reverse direction : start = _seriesInfoList.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 = _seriesInfoList.size(); dir = 1; } LOG4CXX_DEBUG(_logger, " viewport translation:"<_tick._position == Tick::TickPosition::OUTWARDS) { // set room for tick, tickmarks (graduation) and extra space between tickmarks (graduation) and data time. yDelta = getVerticalTickLength(pAxis, panelCharSize.second) + yDelta + (0.5 * yInc); } else { // for the first serie, we add an extra space (half of yInc) to add a sepration with time labels yDelta = yDelta + (0.5 * yInc); } for (int i = start; i != end; i += dir) { LOG4CXX_TRACE(_logger, " virtual axis ["<slabelfunc(generateYLabel, _seriesInfoList[i].get()); // translate viewport : pls_->vpor(vpBounds._x, vpBounds._x + vpBounds._width, vpBounds._y + yDelta, vpBounds._y + vpBounds._height + yDelta); // sets windows for new viewport : pls_->wind(winBounds._x, winBounds._x + winBounds._width, winBounds._y, winBounds._y + winBounds._height); pls_->box(options.c_str(), xtick, nxsub, "", 0, 0); yDelta = yDelta + yInc; } // restore viewport. pls_->vpor(vpBounds._x, vpBounds._x + vpBounds._width, vpBounds._y, vpBounds._y + vpBounds._height); // restore window pls_->wind(winBounds._x, winBounds._x + winBounds._width, winBounds._y, winBounds._y + winBounds._height); // Restore initial color. restoreColor(pls_, lInitialColor, pplot_->_panel->_page->_mode); } void 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->_isFirstLabel = true; unsigned int maxNameLen = _pTimeInfo->_timeTitle.size(); // 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); } char formattedStr [40]; double v = 200800000000000; _maxValueLen = strfqsas(formattedStr, 40, _pTimeInfo->_timeFormat.c_str(), v, TimePlot::qsasconfig); // get decorator parameter value //_decoratorPlot->getDataFromServer(); // build all series names : _seriesInfoList.clear(); for (auto p : _decoratorPlot->_parameterAxesList) { const std::string name = p._originalParamId; LOG4CXX_DEBUG(_logger, " inspecting parameter : " << name); // list asked series : for (auto q : p.getYSerieIndexList(_pParameterValues)) { boost::shared_ptr pSerie = boost::shared_ptr(new SeriesInfo()); std::ostringstream osstr; osstr << name; // display index only if non scalar if(q.getDim1Index() != -1){ osstr << "[" << q.getDim1Index() ; if (q.getDim2Index() != -1) osstr << "," << q.getDim2Index(); osstr << "]"; } pSerie->_name = osstr.str(); if (pSerie->_name.size() > maxNameLen) { maxNameLen = pSerie->_name.size(); } pSerie->_isFirstLabel = true; pSerie->_valuesFormat = getValuesFormat(); //Compute max len pSerie->_parameterData = &((*_pParameterValues)[p.getYSeriePropertiesAt(q).getParamId()]); pSerie->_indexComponents = AMDA::Common::ParameterIndexComponent(q.getDim1Index(),q.getDim2Index()); for (int i = 0; i < pSerie->_parameterData->getSize(); ++i) { const unsigned int len = snprintf(formattedStr, 40, getValuesFormat().c_str(), *pSerie->_parameterData->getValues(pSerie->_indexComponents, i)); _maxValueLen = std::max(_maxValueLen, len); } _seriesInfoList.push_back(pSerie); } } // normalize titles : _pTimeInfo->_maxLen = maxNameLen; for (auto p : _seriesInfoList) { p->_maxLen = maxNameLen; p->_maxValueLen = _maxValueLen; } // 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 = generateOrbitTimeLabel; } /***************************** EXTERNAL METHODS ********************************/ void generateYLabel(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 generateYLabel FUNCTION !!!" << std::endl << std::endl; LOG4CXX_WARN(gLogger, "WARNING !!!! NO DATA FOUND FOR generateYLabel FUNCTION !!"); return; } SeriesInfo* pInfo = static_cast (data); // if value equals min, display _name string // otherwise, format value and display it if (pInfo->_isFirstLabel) { // do not display min value label // but display title : snprintf(label, length, "%*s", pInfo->_maxLen, pInfo->_name.c_str()); pInfo->_isFirstLabel = false; } else { // 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->_indexComponents, crtIndex)); } snprintf(label, length, "%s", labels.str().c_str()); } } } void generateOrbitTimeLabel(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); // get time format and min value // if value equals min, display an empty string // otherwise, format time and display formatted time if (pInfo->_isFirstLabel) { // do not display min value label // but display title : snprintf(label, length, "%*s", pInfo->_maxLen, pInfo->_timeTitle.c_str()); pInfo->_isFirstLabel = false; } else { // 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 */