/* * PanelPlotOutput.cc * * Created on: 29 oct. 2013 * Author: CS */ #include "PanelPlotOutput.hh" #include "ColormapManager.hh" #include "ColorAxis.hh" #include "ParameterManager.hh" #include "Page.hh" #include "ShadesTools.hh" #include "PlotLogger.hh" #include "TimeUtil.hh" #include "ParamMgr.hh" using namespace AMDA::Parameters; using namespace AMDA::Info; namespace plot { /** * Width character proportion is approximatively 0.83 times smaller than height. */ const float PanelPlotOutput::CHAR_RATIO = 5./6.; const float PanelPlotOutput::DEFAULT_TICK_LENGTH_FACTOR = 0.75; // Vertical is for Y axis. const float PanelPlotOutput::VERTICAL_TICK_LENGTH_LIMIT = 0.5; // Horizontal is for X axis. const float PanelPlotOutput::HORIZONTAL_TICK_LENGTH_LIMIT = 0.75; PanelPlotOutput::PanelPlotOutput(AMDA::Parameters::ParameterManager& manager, boost::shared_ptr panel) : _panel(panel), _parameterManager(manager), _pParameterValues(NULL), _timeIntervalListPtr(NULL), _xAxisLegendRowInfo(new std::vector()), _panelPlotXYRatio(1), _leftAxisTickMarkWidth(0), _automaticSerieColorCursor(0) { } PanelPlotOutput::~PanelPlotOutput() { } /** * @brief Calculate character height and width. * @return Returns size of character where first element is width and second height. */ CharSize PanelPlotOutput::getCharacterSize(double pFontScaleFactor) { // Get default char height of plplot PLFLT lDefaultCharHeight, lCurrentCharHeight; _pls->gchr(lDefaultCharHeight, lCurrentCharHeight); // Plplot uses A4 format and so for other format we need to calculate ratio. double lA4Ratio = Page::A4_Y_LENGTH_IN_MM / static_cast(Page::A4_X_LENGTH_IN_MM); float pageWidth = std::get < 0 > (_panel->_page->getSize()); float pageHeight = std::get < 1 > (_panel->_page->getSize()); double lDesiredRatio = pageHeight / pageWidth; double lGap = lDesiredRatio / lA4Ratio; float lCharWidth, lCharHeight; if (_panel->_page->_orientation == PlotCommon::Orientation::LANDSCAPE) { lCharWidth = (lDefaultCharHeight * pFontScaleFactor) / pageHeight; lCharWidth *= lGap; lCharHeight = lCharWidth * (pageWidth / pageHeight); } else { lCharHeight = (lDefaultCharHeight * pFontScaleFactor) / pageHeight; lCharHeight *= lGap; // process font width using largest standard character :'M' PLFLT fontWidth; std::string mchar = "M"; fontWidth = plstrl(mchar.c_str()); lCharWidth = fontWidth / pageHeight; lCharWidth *= lGap; lCharWidth += lCharWidth * (pageWidth / pageHeight); } // Set full character height; lCharWidth *= 2; lCharHeight *= 2; CharSize lCharSize(lCharWidth, lCharHeight); return lCharSize; } double getVerticalTickLength(Axis* pAxis, double charHeight) { float horizontalTickLengthLimit = PanelPlotOutput::HORIZONTAL_TICK_LENGTH_LIMIT; float defaultTickLengthFactor = PanelPlotOutput::DEFAULT_TICK_LENGTH_FACTOR; double tickLengthFactor = pAxis->_tick._lengthFactor; if ((!isnan(tickLengthFactor)) && (tickLengthFactor > horizontalTickLengthLimit)) { // 1 character size is equal to 0.75 tick length. return charHeight + ( ((tickLengthFactor - horizontalTickLengthLimit) * charHeight) / (defaultTickLengthFactor) ); } else if ((isnan(tickLengthFactor)) && (defaultTickLengthFactor > horizontalTickLengthLimit)) { // 1 character size is equal to 0.75 tick length. return charHeight + ( ((defaultTickLengthFactor - horizontalTickLengthLimit) * charHeight) / (defaultTickLengthFactor) ); } else { return charHeight; } } double getHorizontalTickLength (Axis* pAxis, double charHeight) { float verticalTickLengthLimit = PanelPlotOutput::VERTICAL_TICK_LENGTH_LIMIT; float defaultTickLengthFactor = PanelPlotOutput::DEFAULT_TICK_LENGTH_FACTOR; double tickLengthFactor = pAxis->_tick._lengthFactor; if ((!isnan(tickLengthFactor)) && (tickLengthFactor > verticalTickLengthLimit)) { // 1 character size is equal to 0.75 tick length. return charHeight + ( ((tickLengthFactor - verticalTickLengthLimit) * charHeight * PanelPlotOutput::CHAR_RATIO) / (defaultTickLengthFactor) ); } else if ((isnan(tickLengthFactor)) && (defaultTickLengthFactor > verticalTickLengthLimit)) { // 1 character size is equal to 0.75 tick length. return charHeight + ( ((defaultTickLengthFactor - verticalTickLengthLimit) * charHeight * PanelPlotOutput::CHAR_RATIO) / (defaultTickLengthFactor) ); } else { return charHeight; } } void PanelPlotOutput::calculatePlotArea(Bounds& bounds_) { double topSpace = 0; double bottomSpace = 0; double leftSpace = 0; double rightSpace = 0; double titleSpace = 0; // Get character size of panel title. double lFontFactorPanelTitle = getPlFontScaleFactor(_panel->_title._font); CharSize lCharSizePanelTitle(getCharacterSize(lFontFactorPanelTitle)); double panelTitleCharHeight = lCharSizePanelTitle.second; //Init _plotAreaSideSet[PlotCommon::Position::POS_TOP] = false; _plotAreaSideSet[PlotCommon::Position::POS_BOTTOM] = false; _plotAreaSideSet[PlotCommon::Position::POS_RIGHT] = false; _plotAreaSideSet[PlotCommon::Position::POS_LEFT] = false; _plotAreaSideSet[PlotCommon::Position::POS_CENTER] = false; // Set space between panel title and graduation number. if (_panel->_title._text.empty() == false) { LabelRowInfo splittedLegend = Label::getRowNumber(_panel->_title); // Next operand of addition is to set space as many as title is requested to be down. // we add 0.5 because when using plmtex with disposition value of 0, // text is drawn on edge. topSpace += panelTitleCharHeight * (-_panel->getTitlePositionUnits() - 0.5); topSpace += (panelTitleCharHeight * ((1 + Panel::LINE_SPACE_TITLE) * splittedLegend->size())); // Set rest of full character height under title. topSpace += panelTitleCharHeight * (1 - Panel::LINE_SPACE_TITLE); titleSpace = topSpace; } // Get character size for panel. double lFontFactorPanel = getPlFontScaleFactor(_panel->_font); CharSize lCharSizePanel(getCharacterSize(lFontFactorPanel)); double panelCharWidth = lCharSizePanel.first; double panelCharHeight = lCharSizePanel.second; int topTickMarkCharHeight = 0; int bottomTickMarkCharHeight = 0; int leftTickMarkCharWidth = 0; int rightTickMarkCharWidth = 0; float topTickMarkSpace = 0; float bottomTickMarkSpace = 0; float leftTickMarkSpace = 0; float rightTickMarkSpace = 0; // ZAxis must be drawn at last! std::vector>> workingAxesList; for (Axes::iterator it = _panel->_axes.begin(); it != _panel->_axes.end(); ++it) { //Add X and Y axis to the working list boost::shared_ptr lAxis = it->second; if ((lAxis == nullptr) || (lAxis->_isZAxis)) continue; workingAxesList.push_back(std::pair>(it->first,lAxis)); } for (Axes::iterator it = _panel->_axes.begin(); it != _panel->_axes.end(); ++it) { //Add Z axis to the working list boost::shared_ptr lAxis = it->second; if ((lAxis == nullptr) || (!lAxis->_isZAxis)) continue; workingAxesList.push_back(std::pair>(it->first,lAxis)); } // Get each axes information to create plot area bounds (set space for legends). for (std::vector>>::iterator it = workingAxesList.begin(); it != workingAxesList.end(); ++it) { boost::shared_ptr lAxis = it->second; if (lAxis == nullptr) continue; if (!lAxis->_used) continue; // Axis space legends is only took into account when axis is visible // otherwise legend is not drawn. if (lAxis->_visible) { // Get number of line to draw for legend. LabelRowInfo lAxisLegendRowInfo(Label::getRowNumber(lAxis->_legend)); int nbLineLegend = lAxisLegendRowInfo->size(); // If font of legend is not defined, use panel font. Font lFont(lAxis->_legend._font); if (lFont._size == 0) { lFont = _panel->_font; } // Get character size of legend. double lFontFactorLegend = getPlFontScaleFactor(lFont); CharSize lCharSizeLegend(getCharacterSize(lFontFactorLegend)); double legendCharWidth = lCharSizeLegend.first; double legendCharHeight = lCharSizeLegend.second; // Check position of axis. switch (lAxis->_position) { case PlotCommon::Position::POS_BOTTOM: if (lAxis->isZAxis()) LOG4CXX_WARN(gLogger, "PanelPlotOutput::calculatePlotArea: Unrecognized position for z axis '" << lAxis->_position << "'"); // This attribute is used later (in function PanelPlotOutput::drawSeries). if (!lAxis->isZAxis()) _plotAreaSideSet[lAxis->_position] = true; if ((lAxis->_showLegend == true) && (nbLineLegend != 0)) { bottomSpace += legendCharHeight * (2 * nbLineLegend + 1); } bottomTickMarkCharHeight = lAxis->getTickMarkSize().second; // if legend is not set and there is tick mark to draw, add space under tick mark if (bottomSpace == 0 && bottomTickMarkCharHeight != 0) { bottomSpace += panelCharHeight; } // Set space to have tick mark visible. bottomSpace += panelCharHeight * bottomTickMarkCharHeight; // Extra Space for graduation is only took into account if ticks are outwards if(lAxis->_tick._position == Tick::TickPosition::OUTWARDS) { // Add more space for graduation when tick length is higher than 0.75 (default value). bottomTickMarkSpace += getVerticalTickLength(lAxis.get(), panelCharHeight); } else { // When ticks are inward we let space between edge of plot area and tickmarks. bottomTickMarkSpace += panelCharHeight; } bottomSpace += bottomTickMarkSpace; break; case PlotCommon::Position::POS_TOP: if (lAxis->isZAxis()) LOG4CXX_WARN(gLogger, "PanelPlotOutput::calculatePlotArea: Unrecognized position for z axis '" << lAxis->_position << "'"); // This attribute is used later (in function PanelPlotOutput::drawSeries). if (!lAxis->isZAxis()) _plotAreaSideSet[lAxis->_position] = true; if (lAxis->_showLegend == true) { // Check if panel title is set before setting legend required space if (topSpace != 0) { // When panel title is set space between legend/axis and panel title is already added. // So we just need to add space for legend. topSpace += legendCharHeight * (2 * nbLineLegend); } else { // When panel title is not set we must add space above and under legend of axis. topSpace += legendCharHeight * (2 * nbLineLegend + 1); } } topTickMarkCharHeight = lAxis->getTickMarkSize().second; // if legend and title is not set and there is tick mark to draw, add space under tick label if(topSpace == 0 && topTickMarkCharHeight != 0) { topSpace += panelCharHeight; } // Set space for tick marks visibility. topSpace += panelCharHeight * topTickMarkCharHeight; // Extra Space for graduation is only took into account if ticks are outwards if(lAxis->_tick._position == Tick::TickPosition::OUTWARDS) { // Add more space for graduation when tick length is higher than 0.75 (default value). topTickMarkSpace += getVerticalTickLength(lAxis.get(), panelCharHeight); } else { // When ticks are inward we let space between edge of plot area and tickmarks. topTickMarkSpace += panelCharHeight; } topSpace += topTickMarkSpace; break; case PlotCommon::Position::POS_LEFT: // Record axis offset used for multi-axes lAxis->setAxisOffset (leftSpace); if (!lAxis->isZAxis()) { // This attribute is used later (in function PanelPlotOutput::drawSeries). _plotAreaSideSet[lAxis->_position] = true; if ((lAxis->_showLegend == true) && (nbLineLegend != 0)) { leftSpace += legendCharWidth * (2 * nbLineLegend + 1); } leftTickMarkCharWidth = lAxis->getTickMarkSize().first; // Store axis left axis tickmark width (used for layout alignment) _leftAxisTickMarkWidth = leftTickMarkCharWidth; // if legend is not set and there is tick mark to draw, add space at left of tick label if(leftSpace == 0 && leftTickMarkCharWidth != 0) { leftSpace += panelCharHeight; } // Set space for tick label visibility. leftSpace += panelCharHeight * leftTickMarkCharWidth * CHAR_RATIO; // Extra Space for graduation is only took into account if ticks are outwards if(lAxis->_tick._position == Tick::TickPosition::OUTWARDS) { // Add more space for graduation when tick length is higher than 0.75 (default value). leftTickMarkSpace = getHorizontalTickLength(lAxis.get(), panelCharHeight); } else { // Set default space under tick marks (graduation) leftTickMarkSpace += panelCharHeight; } leftSpace += leftTickMarkSpace; } else { leftSpace += estimateZAxisWidth(lAxis); } break; case PlotCommon::Position::POS_RIGHT: // Record axis offset used for multi-axes lAxis->setAxisOffset (rightSpace); if (!lAxis->isZAxis()) { // This attribute is used later (in function PanelPlotOutput::drawSeries). _plotAreaSideSet[lAxis->_position] = true; if ((lAxis->_showLegend == true) && (nbLineLegend != 0)) { rightSpace += legendCharWidth * (2 * nbLineLegend + 1); } rightTickMarkCharWidth = lAxis->getTickMarkSize().first; // if legend is not set and there is tick mark to draw, add space at right of tick label if(rightSpace == 0 && rightTickMarkCharWidth != 0) { rightSpace += panelCharHeight; } // Set space for tick label visibility. rightSpace += panelCharHeight * rightTickMarkCharWidth * CHAR_RATIO; // Extra Space for graduation is only took into account if ticks are outwards if(lAxis->_tick._position == Tick::TickPosition::OUTWARDS) { // Add more space for graduation when tick length is higher than 0.75 (default value). rightTickMarkSpace = getHorizontalTickLength(lAxis.get(), panelCharHeight); } else { // Set default space under tick marks (graduation). rightTickMarkSpace += panelCharHeight; } rightSpace += rightTickMarkSpace; } else { rightSpace += estimateZAxisWidth(lAxis); } break; case PlotCommon::Position::POS_CENTER: default: LOG4CXX_WARN(gLogger, "PanelPlotOutput::drawPlotArea: Unrecognized position for axis '" << lAxis->_position << "'"); } } } // When title, legend and axis tick label are not set, // Just set space: // - for graduation, if (topSpace == 0) { if (bottomTickMarkSpace != 0) { topSpace += bottomTickMarkSpace; // By default plplot set tick mark 1.25 * character height under edge. // To don't have tick touching panel edge we reproduce 0.25 space. topSpace += panelCharHeight * 0.25; } else { topSpace += panelCharHeight; } } else { // When there is no axis attached to a side of plot area, // there is at least graduation to draw (the same as the opposite side) // and so let space for that. // This case is only valid because of title presence. if (topTickMarkSpace == 0) { topSpace += bottomTickMarkSpace; } } // When legend and axis tick label are not set, // Just set space: // - for graduation, if (bottomSpace == 0) { if(topTickMarkSpace != 0) { bottomSpace += topTickMarkSpace; // By default plplot set tick mark 1.25 * character height under edge. // To don't have tick touching panel edge we reproduce 0.25 space. bottomSpace += panelCharHeight * 0.25; } else { bottomSpace += panelCharHeight; } } // When legend and axis tick label are not set, // Just set space: // - for graduation, // - between graduation and edge of panel. if (leftSpace == 0) { // This case occur when axis must not be visible leftSpace += rightTickMarkSpace; leftSpace += panelCharHeight; } // When legend and axis tick label are not set, // Just set space: // - for graduation, // - between graduation and edge of panel. if (rightSpace == 0) { // This case occur when axis must not be visible rightSpace += leftTickMarkSpace; rightSpace += panelCharHeight; } //add params legend right space when outside if (!_panel->_paramsLegendProperties.getLegendLines().empty() && _panel->_paramsLegendProperties.isVisible() && (_panel->_paramsLegendProperties.getPosition() == ParamsLegendPosition::POS_OUTSIDE)) { double lFontFactorLegend = getPlFontScaleFactor(_panel->_paramsLegendProperties.getFont()); CharSize lCharSizeLegend(getCharacterSize(lFontFactorLegend)); double legendCharWidth = lCharSizeLegend.first; //set offset _panel->_paramsLegendProperties.setLeftOffset(rightSpace); //reserve place for legend rightSpace += _panel->_paramsLegendProperties.getEstimateWidth(legendCharWidth); } // Add text legend space if necessary ( maximum 1 text legend by position bool leftTextLegendFound = false; bool rightTextLegendFound = false; bool topTextLegendFound = false; bool bottomTextLegendFound = false; for (auto textLegendProp : _panel->_textLegendPropertiesList) { int textLegendLinesNb = textLegendProp->getTextLinesNb(); if (textLegendLinesNb == 0) continue; double lFontFactorLegend = getPlFontScaleFactor(textLegendProp->getFont()); CharSize lCharSizeLegend(getCharacterSize(lFontFactorLegend)); double legendCharHeight = lCharSizeLegend.second; // For the legend, store it's drawing position and // reserve space for it on the panel // Spacing is determined by the lfollowing principle // - legend text height is 1 x legendCharHeight // - spacing between test lines is 0.5 x legendCharHeight // - 0.5 x legendCharHeight begins and and the "text legend" space // => "text legend" space = (1+0.5) x nb textLines + 0.5 switch (textLegendProp->getPosition()) { case TextLegendPosition::POS_LEFT : if (leftTextLegendFound == false) { textLegendProp->setOffset (leftSpace); leftSpace += (1.5 * textLegendLinesNb + 0.5) * legendCharHeight * CHAR_RATIO; leftTextLegendFound = true; } break; case TextLegendPosition::POS_RIGHT : if (rightTextLegendFound == false) { textLegendProp->setOffset (rightSpace); rightSpace += (1.5 * textLegendLinesNb + 0.5) * legendCharHeight * CHAR_RATIO; rightTextLegendFound = true; } break; case TextLegendPosition::POS_TOP : if (topTextLegendFound == false) { textLegendProp->setOffset (topSpace - titleSpace); topSpace += (1.5 * textLegendLinesNb + 0.5) * legendCharHeight; topTextLegendFound = true; } break; case TextLegendPosition::POS_BOTTOM : if (bottomTextLegendFound == false) { textLegendProp->setOffset (bottomSpace); bottomSpace += (1.5 * textLegendLinesNb + 0.5) * legendCharHeight; bottomTextLegendFound = true; } break; } } // Set specified left, right, top and bottom margins and display a warning // message if computed margins are bigger than specified ones if (_panel->_leftMargin != -1) { if ((_panel->_leftMargin * panelCharWidth) < leftSpace) LOG4CXX_WARN(gLogger, "Left margin for the panel was forced beyond computed size"); leftSpace = _panel->_leftMargin * panelCharWidth; } if (_panel->_rightMargin != -1) { if ((_panel->_rightMargin * panelCharWidth) < rightSpace) LOG4CXX_WARN(gLogger, "Right margin for the panel was forced beyond computed size"); rightSpace = _panel->_rightMargin * panelCharWidth; } if (_panel->_topMargin != -1) { if ((_panel->_topMargin * panelCharHeight) < topSpace) LOG4CXX_WARN(gLogger, "Top margin for the panel was forced beyond computed size"); topSpace = _panel->_topMargin * panelCharHeight; } if (_panel->_bottomMargin != -1) { if ((_panel->_bottomMargin * panelCharHeight) < bottomSpace) LOG4CXX_WARN(gLogger, "Bottom margin for the panel was forced beyond computed size"); bottomSpace = _panel->_bottomMargin * panelCharHeight; } // Update plot area bounds bounds_._x += leftSpace; bounds_._y += bottomSpace; bounds_._width -= (leftSpace + rightSpace); bounds_._height -= (bottomSpace + topSpace); } /** * @brief Compute panel XY ratio used for angular conversions */ void PanelPlotOutput::computePanelPlotXYRatio (void) { // panel plot XY ratio = page ratio * plot area bound size ratio _panelPlotXYRatio = (std::get < 0 > (_panel->_page->getSize()) / std::get < 1 > (_panel->_page->getSize())) * (_plotAreaBounds._width / _plotAreaBounds._height); } /** * @brief Draw legend for each axis. */ void PanelPlotOutput::drawLegends(boost::shared_ptr& pAxis, PlWindow& pPlWindow, Bounds& pPlotAreaSize) { LOG4CXX_DEBUG(gLogger, "Drawing legend for axis "<< pAxis->_id << " : " << pAxis->_legend._text); // Get axis offset in case of multi-axes double axisOffset = pAxis->getAxisOffset(); // Get panel character height. CharSize lPanelCharSize(getCharacterSize(getPlFontScaleFactor(_panel->_font))); double panelCharWidth = lPanelCharSize.first; double panelCharHeight = lPanelCharSize.second; // Get number of line to draw for legend. LabelRowInfo lAxisLegendRowInfo(Label::getRowNumber(pAxis->_legend)); // If font of legend is not defined, use panel font. Font lFont(pAxis->_legend._font); std::vector lStyle(pAxis->_legend._style); if (lFont._size == 0) { lFont = _panel->_font; lStyle = std::vector(); } // Get legend character height. CharSize lLegendCharSize(getCharacterSize(getPlFontScaleFactor(lFont))); double legendCharWidth = lLegendCharSize.first; double legendCharHeight = lLegendCharSize.second; // Check if there is something to draw ! if ((pAxis->_visible == true) && (pAxis->_showLegend == true) && (lAxisLegendRowInfo->size() != 0)) { Color lInitialColor; // If legend has not it's own color set axis color. if ((pAxis->_legend._color._colorIndex == -1) && ((pAxis->_legend._color._red == -1) && (pAxis->_legend._color._green == -1) && (pAxis->_legend._color._blue == -1))) { lInitialColor = changeColor(_pls, pAxis->_color, _panel->_page->_mode); } else { lInitialColor = changeColor(_pls, pAxis->_legend._color, _panel->_page->_mode); } // Set font. _pls->schr(getPlFontDef(lFont), getPlFontScaleFactor(lFont)); // Set font style. _pls->sfont(getPlFontFamily(lFont), getPlFontStyle(lStyle), getPlFontWeight(lStyle)); float lTickMarkSpace = 0; float lPosition = 0; // Depending on the way to draw legend, for BOTTOM position we have to draw from first legend line to last. switch (pAxis->_position) { case PlotCommon::Position::POS_BOTTOM: _xAxisLegendRowInfo = lAxisLegendRowInfo; // Get extra tick mark space (only if they are outwards) if (pAxis->_tick._position == Tick::TickPosition::OUTWARDS) { if ((!isnan(pAxis->_tick._lengthFactor)) && (pAxis->_tick._lengthFactor > HORIZONTAL_TICK_LENGTH_LIMIT)) { // 1 character size is equal to 0.75 tick length. lTickMarkSpace = (pAxis->_tick._lengthFactor - HORIZONTAL_TICK_LENGTH_LIMIT) / (DEFAULT_TICK_LENGTH_FACTOR); } else if ((isnan(pAxis->_tick._lengthFactor)) && (DEFAULT_TICK_LENGTH_FACTOR > HORIZONTAL_TICK_LENGTH_LIMIT)) { // 1 character size is equal to 0.75 tick length. lTickMarkSpace = (DEFAULT_TICK_LENGTH_FACTOR - HORIZONTAL_TICK_LENGTH_LIMIT) / (DEFAULT_TICK_LENGTH_FACTOR); } } // Begin after tick marks; // Needed of setting 1.5 is to be placed at beginning of tick mark. // Getting tick mark size is to let space for tick mark lPosition = (1.5 + lTickMarkSpace + pAxis->getTickMarkSize().second) * (panelCharHeight / legendCharHeight); lPosition += (0.5 - 0.5 * (panelCharHeight / legendCharHeight)); // add space after tick marks; lPosition += 1; // Draw lines, beginning by the first one for (int r=0; r< (int) lAxisLegendRowInfo->size(); r++) { std::string lineToDraw = lAxisLegendRowInfo->at(r); if (r == (int)lAxisLegendRowInfo->size() - 1) { //add unit if defined if (!pAxis->getAxisUnits().empty()) { lineToDraw += " ("; lineToDraw += pAxis->getAxisUnits(); lineToDraw += ")"; } } _pls->mtex(getPlSide(pAxis->_position).c_str(), lPosition, 0.5, 0.5, lineToDraw.c_str()); lPosition += 2; } break; case PlotCommon::Position::POS_TOP: // Get extra tick mark space (only if they are outwards) if (pAxis->_tick._position == Tick::TickPosition::OUTWARDS) { if ((!isnan(pAxis->_tick._lengthFactor)) && (pAxis->_tick._lengthFactor > HORIZONTAL_TICK_LENGTH_LIMIT)) { // 1 character size is equal to 0.75 tick length. lTickMarkSpace = (pAxis->_tick._lengthFactor - HORIZONTAL_TICK_LENGTH_LIMIT) / (DEFAULT_TICK_LENGTH_FACTOR); } else if ((isnan(pAxis->_tick._lengthFactor)) && (DEFAULT_TICK_LENGTH_FACTOR > HORIZONTAL_TICK_LENGTH_LIMIT)) { // 1 character size is equal to 0.75 tick length. lTickMarkSpace = (DEFAULT_TICK_LENGTH_FACTOR - HORIZONTAL_TICK_LENGTH_LIMIT) / (DEFAULT_TICK_LENGTH_FACTOR); } } // Begin after tick marks; lPosition = (1.5 + lTickMarkSpace + pAxis->getTickMarkSize().second) * (panelCharHeight / legendCharHeight); lPosition += (0.5 - 0.5 * (panelCharHeight / legendCharHeight)); // add space after tick marks; lPosition += 1; // Draw lines, beginning by the last one for (int r=(lAxisLegendRowInfo->size() - 1); r>=0; r--) { std::string lineToDraw = lAxisLegendRowInfo->at(r); if (r == (int)lAxisLegendRowInfo->size() - 1) { //add unit if defined if (!pAxis->getAxisUnits().empty()) { lineToDraw += " ("; lineToDraw += pAxis->getAxisUnits(); lineToDraw += ")"; } } _pls->mtex (getPlSide(pAxis->_position).c_str(), lPosition, 0.5, 0.5, lineToDraw.c_str()); lPosition += 2; } break; case PlotCommon::Position::POS_RIGHT: // Depending on the axis offset (used for multi-axes), update viewport and window if (axisOffset != 0) { _pls->vpor(pPlotAreaSize._x, pPlotAreaSize._x + pPlotAreaSize._width + axisOffset, pPlotAreaSize._y, pPlotAreaSize._y + pPlotAreaSize._height); _pls->wind(std::get<0>(pPlWindow), std::get<1>(pPlWindow), std::get<2>(pPlWindow), std::get<3>(pPlWindow)); } // Get extra tick mark space (only if they are outwards) if (pAxis->_tick._position == Tick::TickPosition::OUTWARDS) { if ((!isnan(pAxis->_tick._lengthFactor)) && (pAxis->_tick._lengthFactor > VERTICAL_TICK_LENGTH_LIMIT)) { // 1 character size is equal to 0.75 tick length. lTickMarkSpace = (pAxis->_tick._lengthFactor - VERTICAL_TICK_LENGTH_LIMIT) / (DEFAULT_TICK_LENGTH_FACTOR); } else if ((isnan(pAxis->_tick._lengthFactor)) && (DEFAULT_TICK_LENGTH_FACTOR > VERTICAL_TICK_LENGTH_LIMIT)) { // 1 character size is equal to 0.75 tick length. lTickMarkSpace = (DEFAULT_TICK_LENGTH_FACTOR - VERTICAL_TICK_LENGTH_LIMIT) / (DEFAULT_TICK_LENGTH_FACTOR); } } // Begin after tick marks; lPosition = (1.5 + lTickMarkSpace + (pAxis->getTickMarkSize().first * CHAR_RATIO)) * (panelCharWidth / legendCharWidth); lPosition += (0.5 - 0.5 * (panelCharWidth / legendCharWidth)); // add space after tick marks; lPosition += 1; // Draw lines, beginning by the first one for (int r=0; r< (int) lAxisLegendRowInfo->size(); r++) { std::string lineToDraw = lAxisLegendRowInfo->at(r); if (r == (int)lAxisLegendRowInfo->size() - 1) { //add unit if defined if (!pAxis->getAxisUnits().empty()) { lineToDraw += " ("; lineToDraw += pAxis->getAxisUnits(); lineToDraw += ")"; } } _pls->mtex(getPlSide(pAxis->_position).c_str(), lPosition, 0.5, 0.5, lineToDraw.c_str()); lPosition += 2; } // Depending on the axis offset, restore original viewport and window if (axisOffset != 0) { _pls->vpor(pPlotAreaSize._x, pPlotAreaSize._x + pPlotAreaSize._width, pPlotAreaSize._y, pPlotAreaSize._y + pPlotAreaSize._height); _pls->wind(std::get<0>(pPlWindow), std::get<1>(pPlWindow), std::get<2>(pPlWindow), std::get<3>(pPlWindow)); } break; case PlotCommon::Position::POS_LEFT: // Depending on the axis offset (used for multi-axes), update viewport and window if (axisOffset != 0) { _pls->vpor(pPlotAreaSize._x - axisOffset, pPlotAreaSize._x + pPlotAreaSize._width, pPlotAreaSize._y, pPlotAreaSize._y + pPlotAreaSize._height); _pls->wind(std::get<0>(pPlWindow), std::get<1>(pPlWindow), std::get<2>(pPlWindow), std::get<3>(pPlWindow)); } // Get extra tick mark space (only if they are outwards) if (pAxis->_tick._position == Tick::TickPosition::OUTWARDS) { if ((!isnan(pAxis->_tick._lengthFactor)) && (pAxis->_tick._lengthFactor > VERTICAL_TICK_LENGTH_LIMIT)) { // 1 character size is equal to 0.75 tick length. lTickMarkSpace = (pAxis->_tick._lengthFactor - VERTICAL_TICK_LENGTH_LIMIT) / (DEFAULT_TICK_LENGTH_FACTOR); } else if ((isnan(pAxis->_tick._lengthFactor)) && (DEFAULT_TICK_LENGTH_FACTOR > VERTICAL_TICK_LENGTH_LIMIT)) { // 1 character size is equal to 0.75 tick length. lTickMarkSpace = (DEFAULT_TICK_LENGTH_FACTOR - VERTICAL_TICK_LENGTH_LIMIT) / (DEFAULT_TICK_LENGTH_FACTOR); } } // Begin after tick marks; lPosition = (1.5 + lTickMarkSpace + (pAxis->getTickMarkSize().first * CHAR_RATIO)) * (panelCharWidth / legendCharWidth); lPosition += (0.5 - 0.5 * (panelCharWidth / legendCharWidth)); // add space after tick marks; lPosition += 1; // Draw lines, beginning by the last one for (int r=(lAxisLegendRowInfo->size() - 1); r>=0; r--) { std::string lineToDraw = lAxisLegendRowInfo->at(r); if (r == (int)lAxisLegendRowInfo->size() - 1) { //add unit if defined if (!pAxis->getAxisUnits().empty()) { lineToDraw += " ("; lineToDraw += pAxis->getAxisUnits(); lineToDraw += ")"; } } _pls->mtex (getPlSide(pAxis->_position).c_str(), lPosition, 0.5, 0.5, lineToDraw.c_str()); lPosition += 2; } // Depending on the axis offset, restore original viewport and window if (axisOffset != 0) { _pls->vpor(pPlotAreaSize._x, pPlotAreaSize._x + pPlotAreaSize._width, pPlotAreaSize._y, pPlotAreaSize._y + pPlotAreaSize._height); _pls->wind(std::get<0>(pPlWindow), std::get<1>(pPlWindow), std::get<2>(pPlWindow), std::get<3>(pPlWindow)); } break; case PlotCommon::Position::POS_CENTER: default: LOG4CXX_WARN(gLogger, "PanelPlotOutput::drawPlotArea: Unrecognized position for axis '" << pAxis->_position << "'"); } // Restore initial color. restoreColor(_pls, lInitialColor, _panel->_page->_mode); } } void PanelPlotOutput::drawAxis(boost::shared_ptr pAxis, TickConf& pTickConf, std::string pXAxisOptions, std::string pYAxisOptions) { Color lInitialColor = changeColor(_pls, pAxis->_color, _panel->_page->_mode); // Set axis thickness. _pls->width(pAxis->_thickness); // Set tick (major and minor) length factor PLFLT lTickLenFact = DEFAULT_TICK_LENGTH_FACTOR; if (!isnan(pAxis->_tick._lengthFactor)) { lTickLenFact = pAxis->_tick._lengthFactor; } else { // Nothing to do. } // 0 => keep tick height in millimeters. _pls->smaj(0, lTickLenFact); _pls->smin(0, lTickLenFact); // if options contains "o" character we need to set label function if ((pXAxisOptions.find("o") != std::string::npos) || (pYAxisOptions.find("o") != std::string::npos)) { _pls->slabelfunc(pAxis->_labelGenerator->_function, pAxis->_labelGenerator->_data); } // Set line style (force to be plain) _pls->lsty(static_cast(getPlLineStyle(LineStyle::PLAIN))); // Set font to draw axes. std::vector styles; _pls->schr(getPlFontDef(_panel->_font), getPlFontScaleFactor(_panel->_font)); _pls->sfont(getPlFontFamily(_panel->_font), getPlFontStyle(styles), getPlFontWeight(styles)); // FIX PAC : Use axis legend font if exists (?) /*std::vector styles( pAxis->_legend._style ); Font font = pAxis->_legend._font; if( font._size == 0 ){ // no font is defined for that axis, use panel font as default... font = _panel->_font; } _pls->schr(getPlFontDef(font), getPlFontScaleFactor(font)); _pls->sfont(getPlFontFamily(font), getPlFontStyle(styles), getPlFontWeight(styles));*/ size_t lPos; double xMajorTickSpace = std::get<0>(pTickConf); double xMinorTickNumber = std::get<1>(pTickConf); double yMajorTickSpace = std::get<2>(pTickConf); double yMinorTickNumber = std::get<3>(pTickConf); // if options contains "a" character we need to draw origin separately. if ((lPos = pXAxisOptions.find("a")) != std::string::npos) { pXAxisOptions.replace(lPos, 1, ""); _pls->box("a", xMajorTickSpace, xMinorTickNumber, "", yMajorTickSpace, yMinorTickNumber); } else if ((lPos = pYAxisOptions.find("a")) != std::string::npos) { pYAxisOptions.replace(lPos, 1, ""); _pls->box("", xMajorTickSpace, xMinorTickNumber, "a", yMajorTickSpace, yMinorTickNumber); } else { // Nothing to do. } // Draw axis. _pls->box(pXAxisOptions.c_str(), xMajorTickSpace, xMinorTickNumber, pYAxisOptions.c_str(), yMajorTickSpace, yMinorTickNumber); // Restore initial color. restoreColor(_pls, lInitialColor, _panel->_page->_mode); // Set axis as drawn. pAxis->_drawn = true; } void PanelPlotOutput::drawXAxis(boost::shared_ptr pXAxis, PlWindow& pPlWindow, Bounds& pPlotAreaSize, TickConf& pTickConf) { // Set option to draw origin. std::string lAxisOption = pXAxis->getPlotOpt(); std::string lTmpAxisOption = "xi"; // plplot doesn't provide solution to set space between tick and tick mark. // To do that we need to play with viewport position and size. // So axis will be drawn in two times: // - first draw tick marks // - second draw tick and edge // Custom space between tick marks and tick is only done when tick length is greater than 0.75. // Get extra tick mark space (only if they are outwards) if (pXAxis->_tick._position == Tick::TickPosition::OUTWARDS) { float lTickLengthFactor = 0; if ((!isnan(pXAxis->_tick._lengthFactor)) && (pXAxis->_tick._lengthFactor > HORIZONTAL_TICK_LENGTH_LIMIT)) { lTickLengthFactor = pXAxis->_tick._lengthFactor; } else if ((isnan(pXAxis->_tick._lengthFactor)) && (DEFAULT_TICK_LENGTH_FACTOR > HORIZONTAL_TICK_LENGTH_LIMIT)) { lTickLengthFactor = DEFAULT_TICK_LENGTH_FACTOR; } if (lTickLengthFactor != 0) { // 1 character size is equal to 0.75 tick length. float lTickMarkSpace = (lTickLengthFactor - HORIZONTAL_TICK_LENGTH_LIMIT) * (getCharacterSize(getPlFontScaleFactor(_panel->_font)).second) / (DEFAULT_TICK_LENGTH_FACTOR); size_t lPos; if ((lPos = lAxisOption.find("m")) != std::string::npos) { lTmpAxisOption += "m"; lAxisOption.replace(lPos, 1, ""); } if ((lPos = lAxisOption.find("n")) != std::string::npos) { lTmpAxisOption += "n"; lAxisOption.replace(lPos, 1, ""); } if ((lPos = lAxisOption.find("o")) != std::string::npos) { lTmpAxisOption += "o"; lAxisOption.replace(lPos, 1, ""); } if ((lPos = lAxisOption.find("d")) != std::string::npos) { lTmpAxisOption += "d"; lAxisOption.replace(lPos, 1, ""); } if ((lPos = lAxisOption.find("f")) != std::string::npos) { lTmpAxisOption += "f"; lAxisOption.replace(lPos, 1, ""); } if (pXAxis->_position == PlotCommon::Position::POS_BOTTOM) { _pls->vpor(pPlotAreaSize._x, pPlotAreaSize._x + pPlotAreaSize._width, pPlotAreaSize._y - lTickMarkSpace, pPlotAreaSize._y + pPlotAreaSize._height); } else { _pls->vpor(pPlotAreaSize._x, pPlotAreaSize._x + pPlotAreaSize._width, pPlotAreaSize._y, pPlotAreaSize._y + pPlotAreaSize._height + lTickMarkSpace); } _pls->wind(std::get<0>(pPlWindow), std::get<1>(pPlWindow), std::get<2>(pPlWindow), std::get<3>(pPlWindow)); // Draw tick mark. drawAxis(pXAxis, pTickConf, lTmpAxisOption, ""); } } // Restore default plot area _pls->vpor(pPlotAreaSize._x, pPlotAreaSize._x + pPlotAreaSize._width, pPlotAreaSize._y, pPlotAreaSize._y + pPlotAreaSize._height); // Set window. _pls->wind(std::get<0>(pPlWindow), std::get<1>(pPlWindow), std::get<2>(pPlWindow), std::get<3>(pPlWindow)); // Check if origin must be drawn. if (std::get<0>(pPlWindow) != 0 && std::get<1>(pPlWindow) && pXAxis->_origin == 0) { lAxisOption += "a"; } // Check if opposite plot area side need to be drawn lAxisOption += drawOppositeSide(pXAxis); // Draw rest of axis. drawAxis(pXAxis, pTickConf, lAxisOption, ""); } void PanelPlotOutput::drawYAxis(boost::shared_ptr pYAxis, PlWindow& pPlWindow, Bounds& pPlotAreaSize, TickConf& pTickConf) { // Get axis offset in case of multi-axes double axisOffset = pYAxis->getAxisOffset(); // Set option to draw origin ("v" is set to have numeric labels parallel to X axis). std::string lAxisOption = pYAxis->getPlotOpt(); std::string lTmpAxisOption = "xiv"; // plplot doesn't provide solution to set space between tick and tick mark. // To do that we need to play with viewport position and size. // So axis will be drawn in two times: // - first draw tick marks // - second draw tick and edge // Custom space between tick marks and tick is only done when tick length is greater than 0.5. // Get extra tick mark space (only if they are outwards) if (pYAxis->_tick._position == Tick::TickPosition::OUTWARDS) { float lTickLengthFactor = 0; if ((!isnan(pYAxis->_tick._lengthFactor)) && (pYAxis->_tick._lengthFactor > VERTICAL_TICK_LENGTH_LIMIT)) { lTickLengthFactor = pYAxis->_tick._lengthFactor; } else if ((isnan(pYAxis->_tick._lengthFactor)) && (DEFAULT_TICK_LENGTH_FACTOR > VERTICAL_TICK_LENGTH_LIMIT)) { lTickLengthFactor = DEFAULT_TICK_LENGTH_FACTOR; } if (lTickLengthFactor != 0) { // 1 character size is equal to 0.75 tick length. double lTickMarkSpace = (lTickLengthFactor - VERTICAL_TICK_LENGTH_LIMIT) * (getCharacterSize(getPlFontScaleFactor(_panel->_font)).second) / (DEFAULT_TICK_LENGTH_FACTOR); size_t lPos; if ((lPos = lAxisOption.find("m")) != std::string::npos) { lTmpAxisOption += "m"; lAxisOption.replace(lPos, 1, ""); } if ((lPos = lAxisOption.find("n")) != std::string::npos) { lTmpAxisOption += "n"; lAxisOption.replace(lPos, 1, ""); } if ((lPos = lAxisOption.find("o")) != std::string::npos) { lTmpAxisOption += "o"; lAxisOption.replace(lPos, 1, ""); } if ((lPos = lAxisOption.find("f")) != std::string::npos) { lTmpAxisOption += "f"; lAxisOption.replace(lPos, 1, ""); } if (lAxisOption.find("l") != std::string::npos) { // Do not remove on original axis option // because option is needed to draw logarithmic tick. lTmpAxisOption += "l"; } // Set plot area used to draw tick mark if (pYAxis->_position == PlotCommon::Position::POS_LEFT) { _pls->vpor(pPlotAreaSize._x - lTickMarkSpace - axisOffset, pPlotAreaSize._x + pPlotAreaSize._width, pPlotAreaSize._y, pPlotAreaSize._y + pPlotAreaSize._height); } else { _pls->vpor(pPlotAreaSize._x, pPlotAreaSize._x + pPlotAreaSize._width + lTickMarkSpace + axisOffset, pPlotAreaSize._y, pPlotAreaSize._y + pPlotAreaSize._height); } _pls->wind(std::get<0>(pPlWindow), std::get<1>(pPlWindow), std::get<2>(pPlWindow), std::get<3>(pPlWindow)); // Draw tick mark. drawAxis(pYAxis, pTickConf, "", lTmpAxisOption); } } // Set plot area used to draw tick if (pYAxis->_position == PlotCommon::Position::POS_LEFT) { _pls->vpor(pPlotAreaSize._x - axisOffset, pPlotAreaSize._x + pPlotAreaSize._width, pPlotAreaSize._y, pPlotAreaSize._y + pPlotAreaSize._height); } else { _pls->vpor(pPlotAreaSize._x, pPlotAreaSize._x + pPlotAreaSize._width + axisOffset, pPlotAreaSize._y, pPlotAreaSize._y + pPlotAreaSize._height); } // Set window. _pls->wind(std::get<0>(pPlWindow), std::get<1>(pPlWindow), std::get<2>(pPlWindow), std::get<3>(pPlWindow)); // Check if origin must be drawn. if (std::get<0>(pPlWindow) != 0 && std::get<1>(pPlWindow) && pYAxis->_origin == 0) { lAxisOption += "a"; } // Check if opposite plot area side need to be drawn lAxisOption += drawOppositeSide(pYAxis); // Draw rest of axis. drawAxis(pYAxis, pTickConf, "", lAxisOption); // Restore default plot area _pls->vpor(pPlotAreaSize._x, pPlotAreaSize._x + pPlotAreaSize._width, pPlotAreaSize._y, pPlotAreaSize._y + pPlotAreaSize._height); // Set window. _pls->wind(std::get<0>(pPlWindow), std::get<1>(pPlWindow), std::get<2>(pPlWindow), std::get<3>(pPlWindow)); } #define COLORBAR_X_OFFSET 0.01 #define COLORBAR_BODY_WIDTH 0.0375 double PanelPlotOutput::estimateZAxisWidth(boost::shared_ptr pZAxis) { double axisWidth = (COLORBAR_X_OFFSET + COLORBAR_BODY_WIDTH) * _panel->_bounds._width; double lFontFactor = getPlFontScaleFactor(_panel->_font); CharSize lCharSize(getCharacterSize(lFontFactor)); LabelRowInfo lAxisLegendRowInfo(Label::getRowNumber(pZAxis->_legend)); int nbLineLegend = lAxisLegendRowInfo->size(); if ((pZAxis->_showLegend == true) && (nbLineLegend != 0)) { if (_panel->_page->_orientation == PlotCommon::Orientation::LANDSCAPE) axisWidth += lCharSize.second * (2 * nbLineLegend + 1); else axisWidth += lCharSize.first * (2 * nbLineLegend + 1); } return axisWidth; } void PanelPlotOutput::drawZAxis(boost::shared_ptr pZAxis, PlWindow& /*pPlWindow*/, Bounds& /*pPlotAreaSize*/, TickConf& /*pTickConf*/) { Color lInitialColor; _pls->gcol0(0, lInitialColor._red, lInitialColor._green, lInitialColor._blue); // Set axis thickness. _pls->width(pZAxis->_thickness); // Set line style (force to be plain) _pls->lsty(static_cast(getPlLineStyle(LineStyle::PLAIN))); // Set font to draw axes. std::vector styles; _pls->schr(getPlFontDef(_panel->_font), getPlFontScaleFactor(_panel->_font)); _pls->sfont(getPlFontFamily(_panel->_font), getPlFontStyle(styles), getPlFontWeight(styles)); std::string lAxisOption = pZAxis->getPlotOpt(); std::string lAxisLegend = pZAxis->_legend._text; PLFLT colorbar_width, colorbar_height; const int cont_color = 0; const PLFLT cont_width = 0.0; //set legend const PLINT n_labels = (lAxisLegend.empty()) ? 0 : 1; std::string label = lAxisLegend; boost::replace_all(label, Label::DELIMITER, "\n"); const char *labels[n_labels]; if (!lAxisLegend.empty()) labels[0] = label.c_str(); //set axis options const PLINT n_axis_opts = 1; const char *axis_opts[n_axis_opts] = { lAxisOption.c_str(), }; PLFLT axis_ticks[n_axis_opts] = { 0 }; PLINT axis_subticks[n_axis_opts] = { 0 }; // if options contains "o" character we need to set label function if (lAxisOption.find("o") != std::string::npos) { _pls->slabelfunc(pZAxis->_labelGenerator->_function, pZAxis->_labelGenerator->_data); } //set nb og colors const int nbCol = 20; PLINT num_values[1]= { nbCol+1 }; Range r = pZAxis->getRange(); PLFLT *shedge = new PLFLT[nbCol+1]; ShadesTools::computeEdgesLevels(r,nbCol+1,shedge); PLFLT *values[1]; values[0] = shedge; //set color map _pls->spal1( ColormapManager::getInstance().getColorAxis(pZAxis->_color._colorMapIndex).c_str(), true); //set color bar and legend position PLINT position = PL_POSITION_OUTSIDE | PL_POSITION_VIEWPORT; PLINT label_opts[1]; switch (pZAxis->_position) { case PlotCommon::Position::POS_LEFT : position |= PL_POSITION_LEFT; label_opts[0] = PL_COLORBAR_LABEL_LEFT; break; case PlotCommon::Position::POS_RIGHT : position |= PL_POSITION_RIGHT; label_opts[0] = PL_COLORBAR_LABEL_RIGHT; break; default: { std::stringstream lError; lError << "PanelPlotOutput::drawZAxis : Bad Z axis position"; BOOST_THROW_EXCEPTION( PanelPlotOutputException() << AMDA::ex_msg(lError.str())); } } _pls->colorbar( &colorbar_width, &colorbar_height, PL_COLORBAR_GRADIENT, //PL_COLORBAR_SHADE, position, COLORBAR_X_OFFSET + pZAxis->getAxisOffset(), 0.0, COLORBAR_BODY_WIDTH, 0.875, 0, /*bg_color*/ 1, 1, 0.0, 0.0, cont_color, cont_width, n_labels, label_opts, labels, n_axis_opts, axis_opts, axis_ticks, axis_subticks, num_values, (const PLFLT * const *) values ); //reset color map _pls->spal1( ColormapManager::getInstance().get(_panel->_page->_mode, ColormapManager::DEFAULT_COLORMAP_1).c_str(), true); _pls->scol0(0, lInitialColor._red, lInitialColor._green, lInitialColor._blue); delete [] shedge; // Restore initial color. restoreColor(_pls, lInitialColor, _panel->_page->_mode); // Set axis as drawn. pZAxis->_drawn = true; } Range PanelPlotOutput::getXAxisRange (SeriesProperties& pSeries, boost::shared_ptr pAxis) { if (pSeries.hasXAxis() && pAxis.get() == nullptr) { std::stringstream lError; lError << "PanelPlotOutput::drawSeries" << ": X axis with id '" << pSeries.getXAxisId() << "' not found."; BOOST_THROW_EXCEPTION( PanelPlotOutputException() << AMDA::ex_msg(lError.str())); } else if(pSeries.hasXAxis()) { // fill X range (plplot window). return (pAxis->getRange()); } return Range(); } Range PanelPlotOutput::getYAxisRange (SeriesProperties& pSeries, boost::shared_ptr pAxis) { if (pSeries.hasYAxis() && pAxis.get() == nullptr) { std::stringstream lError; lError << "PanelPlotOutput::drawSeries" << ": Y axis with id '" << pSeries.getYAxisId() << "' not found."; BOOST_THROW_EXCEPTION( PanelPlotOutputException() << AMDA::ex_msg(lError.str())); } else if(pSeries.hasYAxis()) { // fill Y range (plplot window). return (pAxis->getRange()); } return Range(); } Range PanelPlotOutput::getZAxisRange (SeriesProperties& pSeries, boost::shared_ptr pAxis) { if (pSeries.hasZAxis() && pAxis.get() == nullptr) { std::stringstream lError; lError << "PanelPlotOutput::drawSeries" << ": Z axis with id '" << pSeries.getYAxisId() << "' not found."; BOOST_THROW_EXCEPTION( PanelPlotOutputException() << AMDA::ex_msg(lError.str())); } else if(pSeries.hasZAxis()) { // fill Z range (plplot window). return (pAxis->getRange()); } return Range(); } /** * Convert an X axis string value to a double X value */ double PanelPlotOutput::convertXAxisValue(const std::string &value) { return std::stod(value); } /** * Convert an Y axis string value to a double Y value */ double PanelPlotOutput::convertYAxisValue(const std::string &value) { return std::stod(value); } /** * Get colored value associated to a data value. Return false if the value is filtered. */ bool PanelPlotOutput::getColoredValue(double value, double filterMin, double filterMax, PLFLT &col) { boost::shared_ptr lZAxis = _panel->getColorAxis(); if (lZAxis == nullptr) return false; bool filterMinMax = (isNAN (filterMin) == false) || (isNAN (filterMax) == false); Range r = lZAxis->getRange(); if (isnan(value)) return false; if ((filterMinMax == true) && ((value < filterMin) || (value > filterMax))) return false; double correctedValue = value; if (lZAxis->_scale == Axis::Scale::LOGARITHMIC) { if (correctedValue <= 0) return false; correctedValue = log10(correctedValue); } col=(correctedValue-r.getMin())/(r.getMax() - r.getMin()); return true; } /** * Draw list of symbols */ void PanelPlotOutput::drawSymbols(SymbolType pType, int pSize, double pFactor, Color pColor, int pNbData, double* pXData, double* pYData, double* pZData, double filterZMin, double filterZMax) { if (pType == SymbolType::NO) return; Color lInitialColor; // Set color. lInitialColor = changeColor(_pls, pColor, _panel->_page->_mode); _pls->sfont(getPlFontFamily(_panel->_page->_font), getPlFontStyle(_panel->_page->_font), getPlFontWeight(_panel->_page->_font)); // Set size. _pls->ssym(pSize, pFactor); // Get colored axis boost::shared_ptr lZAxis = _panel->getColorAxis(); //draw symbols if ((pZData == NULL) || (lZAxis == nullptr)) _pls->poin(pNbData, pXData, pYData, static_cast(pType)); else { //set color map _pls->spal1( ColormapManager::getInstance().getColorAxis(lZAxis->_color._colorMapIndex).c_str(), true); //get specific colors for min / max values Color minValColor = lZAxis->getMinValColor(); Color maxValColor = lZAxis->getMaxValColor(); PLFLT col; for (int i = 0; i < pNbData; ++i) { if (!getColoredValue(pZData[i], filterZMin, filterZMax, col)) continue; if(col <= 0.) { if (minValColor.isSet()) { restoreColor(_pls, minValColor, _panel->_page->_mode); _pls->poin(1, &pXData[i], &pYData[i], static_cast(pType)); _pls->spal1(ColormapManager::getInstance().getColorAxis(lZAxis->_color._colorMapIndex).c_str(), true); } else { _pls->col1(0); _pls->poin(1, &pXData[i], &pYData[i], static_cast(pType)); } } else if (col>=1.) { if (maxValColor.isSet()) { restoreColor(_pls, maxValColor, _panel->_page->_mode); _pls->poin(1, &pXData[i], &pYData[i], static_cast(pType)); _pls->spal1(ColormapManager::getInstance().getColorAxis(lZAxis->_color._colorMapIndex).c_str(), true); } else { _pls->col1(1); _pls->poin(1, &pXData[i], &pYData[i], static_cast(pType)); } } else { _pls->col1(col); _pls->poin(1, &pXData[i], &pYData[i], static_cast(pType)); } } } // Restore color. restoreColor(_pls, lInitialColor, _panel->_page->_mode); } /** * Draw list of lines */ void PanelPlotOutput::drawLines(LineType pType, LineStyle pStyle, int pWidth, Color& pColor, int pNbData, double* pXData, double* pYData, double* pZData, double filterZMin, double filterZMax) { if (pType == LineType::EMPTY) return; if (pType != LineType::LINE) { BOOST_THROW_EXCEPTION(PanelPlotOutputException() << AMDA::ex_msg("HISTO line type can't be used in time plot. Use LINE or EMPTY type")); } Color lInitialColor; // Set color. lInitialColor = changeColor(_pls, pColor, _panel->_page->_mode); // Set line type. _pls->lsty(static_cast(getPlLineStyle(pStyle))); // Set line thickness. _pls->width(pWidth); // Get colored axis boost::shared_ptr lZAxis = _panel->getColorAxis(); // draw lines. if ((pZData == NULL) || (lZAxis == nullptr)) { _pls->line(pNbData, pXData, pYData); } else { //set color map _pls->spal1( ColormapManager::getInstance().getColorAxis(lZAxis->_color._colorMapIndex).c_str(), true); //get specific colors for min / max values Color minValColor = lZAxis->getMinValColor(); Color maxValColor = lZAxis->getMaxValColor(); PLFLT col; for (int i = 0; i < pNbData - 1; ++i) { if (!getColoredValue(pZData[i], filterZMin, filterZMax, col)) continue; if(col <= 0.) { if (minValColor.isSet()) { restoreColor(_pls, minValColor, _panel->_page->_mode); _pls->line(2, &pXData[i], &pYData[i]); _pls->spal1(ColormapManager::getInstance().getColorAxis(lZAxis->_color._colorMapIndex).c_str(), true); } else { _pls->col1(0); _pls->line(2, &pXData[i], &pYData[i]); } } else if (col>=1.) { if (maxValColor.isSet()) { restoreColor(_pls, maxValColor, _panel->_page->_mode); _pls->line(2, &pXData[i], &pYData[i]); _pls->spal1(ColormapManager::getInstance().getColorAxis(lZAxis->_color._colorMapIndex).c_str(), true); } else { _pls->col1(1); _pls->line(2, &pXData[i], &pYData[i]); } } else { _pls->col1(col); _pls->line(2, &pXData[i], &pYData[i]); } } } // Restore color. restoreColor(_pls, lInitialColor, _panel->_page->_mode); } /* * @brief Draw a matrix */ void PanelPlotOutput::drawMatrix(MatrixGrid& matrixGrid, double minDataVal, double maxDataVal, Color minValColor, Color maxValColor, int colorMapIndex, bool dataValueIsColorIndex) { //set color map _pls->spal1( ColormapManager::getInstance().getColorAxis(colorMapIndex).c_str(), true); Color lInitialColor; _pls->gcol0(0, lInitialColor._red, lInitialColor._green, lInitialColor._blue); //plot matrix PLFLT x[4], y[4]; PLFLT col; for ( auto part : matrixGrid ) { x[0] = part.x[0]; x[1] = part.x[1]; x[2] = x[1]; x[3] = x[0]; y[0] = part.y[0]; y[1] = y[0]; y[2] = part.y[1]; y[3] = y[2]; if (isNAN(part.value)) continue; if (!dataValueIsColorIndex) { if (!getColoredValue(part.value, minDataVal, maxDataVal, col)) continue; } else { Color dataValueColor(colorMapIndex, (int)part.value); restoreColor(_pls, dataValueColor, _panel->_page->_mode); _pls->fill(4, x, y); _pls->spal1(ColormapManager::getInstance().getColorAxis(colorMapIndex).c_str(), true); continue; } if(col <= 0.) { if (minValColor.isSet()) { restoreColor(_pls, minValColor, _panel->_page->_mode); _pls->fill(4, x, y); _pls->spal1(ColormapManager::getInstance().getColorAxis(colorMapIndex).c_str(), true); } else { _pls->col1(0); _pls->fill(4, x, y); } } else if (col>=1.) { if (maxValColor.isSet()) { restoreColor(_pls, maxValColor, _panel->_page->_mode); _pls->fill(4, x, y); _pls->spal1(ColormapManager::getInstance().getColorAxis(colorMapIndex).c_str(), true); } else { _pls->col1(1); _pls->fill(4, x, y); } } else { _pls->col1(col); _pls->fill(4, x, y); } } //restore to initial color context _pls->spal1( ColormapManager::getInstance().get(_panel->_page->_mode, ColormapManager::DEFAULT_COLORMAP_1).c_str(), true); _pls->scol0(0, lInitialColor._red, lInitialColor._green, lInitialColor._blue); // Restore color. restoreColor(_pls, lInitialColor, _panel->_page->_mode); } /** * Draw errors segments */ void PanelPlotOutput::drawYErrors ( LineType pType, LineStyle pStyle, int pWidth, Color& pColor, int pNbData, double* pXData, double* pYMinData, double* pYMaxData ) { if (pType == LineType::EMPTY) return; if (pType != LineType::LINE) { BOOST_THROW_EXCEPTION(PanelPlotOutputException() << AMDA::ex_msg("HISTO line type can't be used in time plot. Use LINE or EMPTY type")); } Color lInitialColor; // Set color. lInitialColor = changeColor(_pls, pColor, _panel->_page->_mode); // Set line type. _pls->lsty(static_cast(getPlLineStyle(pStyle))); // Set line thickness. _pls->width(pWidth); // Set the default tich with _pls->smin (0, 0.5); // Draw error bars _pls->erry(pNbData, pXData, pYMinData, pYMaxData); // Restore color. restoreColor(_pls, lInitialColor, _panel->_page->_mode); } /** * Draws X constant lines linked to axis */ void PanelPlotOutput::drawXConstantLines(boost::shared_ptr pAxis, PlWindow& pPlWindow) { double x [2], y [2]; _pls->wind(std::get<0>(pPlWindow), std::get<1>(pPlWindow), std::get<2>(pPlWindow), std::get<3>(pPlWindow)); for (auto constantLine : pAxis->_constantLines) { if (constantLine->isDrawn()) continue; x[0] = convertXAxisValue (constantLine->getValue()); x[1] = convertXAxisValue (constantLine->getValue()); y[0] = std::get<2>(pPlWindow); y[1] = std::get<3>(pPlWindow); if (pAxis->_scale == Axis::Scale::LOGARITHMIC) { x[0] = log10 (x[0]); x[1] = log10 (x[1]); } drawLines( LineType::LINE, constantLine->getStyle(), constantLine->getWidth(), constantLine->getColor(), 2, x, y); constantLine->setDrawn(true); } } /** * Draws Y constant lines linked to axis */ void PanelPlotOutput::drawYConstantLines(boost::shared_ptr pAxis, PlWindow& pPlWindow) { double x [2], y [2]; _pls->wind(std::get<0>(pPlWindow), std::get<1>(pPlWindow), std::get<2>(pPlWindow), std::get<3>(pPlWindow)); for (auto constantLine : pAxis->_constantLines) { if (constantLine->isDrawn()) continue; x[0] = std::get<0>(pPlWindow); x[1] = std::get<1>(pPlWindow); y[0] = convertYAxisValue(constantLine->getValue()); y[1] = convertYAxisValue(constantLine->getValue()); if (pAxis->_scale == Axis::Scale::LOGARITHMIC) { y[0] = log10 (y[0]); y[1] = log10 (y[1]); } drawLines( LineType::LINE, constantLine->getStyle(), constantLine->getWidth(), constantLine->getColor(), 2, x, y); constantLine->setDrawn(true); } } /** * Draws TextPlots */ void PanelPlotOutput::drawTextPlots(boost::shared_ptr pXAxis, boost::shared_ptr pYAxis, PlWindow& pPlWindow, const TextPlots &textPlots) { double x, y; double x1, x2, y1, y2; x1 = std::get<0>(pPlWindow); x2 = std::get<1>(pPlWindow); y1 = std::get<2>(pPlWindow); y2 = std::get<3>(pPlWindow); _pls->wind(x1, x2, y1, y2); for (auto textPlot: textPlots) { if (textPlot->isDrawn()) continue; if (!textPlot->_xAxisId.empty() && (textPlot->_xAxisId.compare(pXAxis->_id) != 0)) continue; if (!textPlot->_yAxisId.empty() && (textPlot->_yAxisId.compare(pYAxis->_id) != 0)) continue; // Compute x y position for the text if (textPlot->_x.find("%") != std::string::npos) { // Remove % before conversion int pos = textPlot->_x.find("%"); std::string xPos = textPlot->_x; xPos.erase(pos); // Compute x value as a percent of the x value x = x1 + (x2-x1) * std::stod (xPos) / 100.0; } else { x = convertXAxisValue(textPlot->_x); } if (textPlot->_y.find("%") != std::string::npos) { // Remove % before conversion int pos = textPlot->_y.find("%"); std::string yPos = textPlot->_y; yPos.erase(pos); // Compute x value as a percent of the x value y = y1 + (y2-y1) * std::stod (yPos) / 100.0; } else { y = convertYAxisValue(textPlot->_y); } // Select font family, size, style and weight _pls->schr (getPlFontDef(textPlot->_font), getPlFontScaleFactor(textPlot->_font)); _pls->sfont (getPlFontFamily(textPlot->_font), getPlFontStyle(textPlot->_font), getPlFontWeight(textPlot->_font)); // Set color Color lInitialColor = changeColor(_pls, textPlot->_color, _panel->_page->_mode); // Compute dx & dy orientation double dx = cos(textPlot->_angle*M_PI/180) * (x2-x1)/10.0; double dy = sin(textPlot->_angle*M_PI/180) * (y2-y1)/10.0 * _panelPlotXYRatio; // Draw text _pls->ptex (x, y, dx, dy, textPlot->_align, textPlot->_text.c_str()); // Restore initial color. restoreColor(_pls, lInitialColor, _panel->_page->_mode); textPlot->setDrawn(true); } } /* * @brief Draw interval */ void PanelPlotOutput::drawSerieInterval(SeriesProperties& pSeries, double* xValues, double* yValues, double* timeValues, int nbValues, int intervalIndex) { LOG4CXX_DEBUG(gLogger, "PanelPlotOutput::drawSerieInterval"); IntervalTickProperties itProps = pSeries.getIntervalTickProperties(); // Draw Interval ticks if required ! if (itProps.getMode() == IntervalTickMode::NONE) return; // Get X and Y axis. boost::shared_ptr lXAxis(_panel->getAxis(pSeries.getXAxisId())); boost::shared_ptr lYAxis(_panel->getAxis(pSeries.getYAxisId())); Range lXRange = getXAxisRange (pSeries, lXAxis); Range lYRange = getYAxisRange (pSeries, lYAxis); PlWindow lPlWindow = PlWindow(lXRange.getMin(), lXRange.getMax(), lYRange.getMin(), lYRange.getMax()); //prepare data for plot double xData[2] = {NAN,NAN}; double yData[2] = {NAN,NAN}; double timeData[2] = {NAN,NAN}; //skip NAN values for (int i = 0; i < nbValues; ++i) if (!isNAN(xValues[i]) && !isNAN(yValues[i])) { xData[0] = xValues[i]; yData[0] = yValues[i]; timeData[0] = timeValues[i]; break; } for (int i = nbValues - 1; i >= 0; --i) if (!isNAN(xValues[i]) && !isNAN(yValues[i])) { xData[1] = xValues[i]; yData[1] = yValues[i]; timeData[1] = timeValues[i]; break; } //draw symbols drawSymbols( itProps.getSymbol().getType(), itProps.getSymbol().getSize(), 1., itProps.getSymbol().getColor(), 2, (double*)&xData, (double*)&yData, NULL); if (itProps.getMode() == IntervalTickMode::SYMBOL_ONLY) return; //Add start interval info to a list of TextPlot TextPlots textPlots; TextPlot baseTextPlot; baseTextPlot._color = itProps.getColor(); baseTextPlot._font = itProps.getFont(); boost::shared_ptr startTextPlot (new TextPlot (baseTextPlot)); double xPos = (xData[0]-lXRange.getMin()) / (lXRange.getMax()-lXRange.getMin())*100; startTextPlot->_x = boost::lexical_cast(xPos); startTextPlot->_x += "%"; double yPos = (yData[0]-lYRange.getMin()) / (lYRange.getMax()-lYRange.getMin())*100 + 1.5; startTextPlot->_y = boost::lexical_cast(yPos); startTextPlot->_y += "%"; if (itProps.getMode() == IntervalTickMode::INTERVAL_INDEX) { startTextPlot->_text = "I"; startTextPlot->_text += std::to_string(intervalIndex); } else startTextPlot->_text = AMDA::TimeUtil::formatTimeDateInIso(timeData[0]); textPlots.push_back(startTextPlot); if (itProps.getMode() != IntervalTickMode::START_TIME) { boost::shared_ptr stopTextPlot (new TextPlot (baseTextPlot)); xPos = (xData[1]-lXRange.getMin()) / (lXRange.getMax()-lXRange.getMin())*100; stopTextPlot->_x = boost::lexical_cast(xPos); stopTextPlot->_x += "%"; yPos = (yData[1]-lYRange.getMin()) / (lYRange.getMax()-lYRange.getMin())*100 + 1.5; stopTextPlot->_y = boost::lexical_cast(yPos); stopTextPlot->_y += "%"; if (itProps.getMode() == IntervalTickMode::INTERVAL_INDEX) stopTextPlot->_text = startTextPlot->_text; else stopTextPlot->_text = AMDA::TimeUtil::formatTimeDateInIso(timeData[1]); textPlots.push_back(stopTextPlot); } drawTextPlots (lXAxis, lYAxis, lPlWindow, textPlots); } /** * @brief Draw parameters legend. */ void PanelPlotOutput::drawParamsLegend(void) { ParamsLegendProperties &legendProp = _panel->_paramsLegendProperties; if (!legendProp.isVisible() || legendProp.isDrawn()) return; std::list lines = legendProp.getLegendLines(); if (lines.empty()) return; //init arguments to call pllegend function PLINT nbLines = lines.size(); PLINT opt_array[nbLines]; PLINT text_colors[nbLines]; PLINT line_colors[nbLines]; PLINT line_styles[nbLines]; PLFLT line_widths[nbLines]; PLINT symbol_numbers[nbLines]; PLINT symbol_colors[nbLines]; PLFLT symbol_scales[nbLines]; const char *symbols[nbLines]; const char *texts[nbLines]; int index = 0; std::string symbol_str[nbLines]; for (auto line : lines) { opt_array[index] = 0; //set line options if (!legendProp.isOnlyText() && line.isLineVisible()) { opt_array[index] |= PL_LEGEND_LINE; line_colors[index] = legendProp.getColorIndex(_pls.get(),line.getLineProperties().getColor()); line_styles[index] = static_cast(getPlLineStyle(line.getLineProperties().getStyle())); line_widths[index] = line.getLineProperties().getWidth(); } //set symbols options if (!legendProp.isOnlyText() && line.isSymbolsVisible()) { opt_array[index] |= PL_LEGEND_SYMBOL; symbol_numbers[index] = LEGEND_NB_SYMBOLS_TO_DRAW; symbol_colors[index] = legendProp.getColorIndex(_pls.get(),line.getSymbolProperties().getColor()); symbol_scales[index] = (double)line.getSymbolProperties().getSize() / Font::DEFAULT_CHARACTER_SIZE; std::stringstream workingstr; workingstr << "#("<< fontTypeToSymSymbol[line.getSymbolProperties().getType()] << ")"; symbol_str[index] = workingstr.str(); symbols[index] = symbol_str[index].c_str(); } text_colors[index] = legendProp.getColorIndex(_pls.get(),line.getTextColor(legendProp.isOnlyText())); texts[index] = line.getText(); ++index; } PLFLT legend_width, legend_height; PLINT opt = PL_LEGEND_BACKGROUND; //show or hide bounding box if (legendProp.isBorderVisible()) opt |= PL_LEGEND_BOUNDING_BOX; PLINT position = PL_POSITION_VIEWPORT; switch (legendProp.getPosition()) { case ParamsLegendPosition::POS_INSIDE : position |= PL_POSITION_INSIDE | PL_POSITION_TOP | PL_POSITION_RIGHT; break; case ParamsLegendPosition::POS_OUTSIDE : position |= PL_POSITION_OUTSIDE | PL_POSITION_RIGHT; break; default : LOG4CXX_WARN(gLogger, "Legend position not implemented => set to POS_INSIDE"); position |= PL_POSITION_INSIDE | PL_POSITION_TOP | PL_POSITION_RIGHT; } //define offset PLFLT x_offset = 0.0; PLFLT y_offset = 0.0; switch (legendProp.getPosition()) { case ParamsLegendPosition::POS_INSIDE : x_offset = LEGEND_INSIDE_OFFSET; y_offset = LEGEND_INSIDE_OFFSET * _panelPlotXYRatio; break; case ParamsLegendPosition::POS_OUTSIDE : x_offset = 0.0; y_offset = 0.0; break; default : x_offset = LEGEND_INSIDE_OFFSET; y_offset = LEGEND_INSIDE_OFFSET * _panelPlotXYRatio; } //show or hide line and symbols PLFLT plot_width = 0; if (!legendProp.isOnlyText()) plot_width = LEGEND_LINE_SYM_WIDTH; //background color = panel color PLINT bg_color = legendProp.getColorIndex(_pls.get(),_panel->_backgroundColor); //bounding box PLINT bb_color = legendProp.getColorIndex(_pls.get(),legendProp.getBorderColor()); PLINT bb_style = 1; //text option PLFLT text_offset = 0.0; if (!legendProp.isOnlyText()) text_offset = LEGEND_TEXT_OFFSET; PLFLT text_scale = getPlFontScaleFactor(legendProp.getFont()); PLFLT text_spacing = LEGEND_TEXT_SPACING; PLFLT text_justification = LEGEND_TEXT_JUSTIFICATION; //colored boxes not used PLINT *box_colors = NULL; PLINT *box_patterns = NULL; PLFLT *box_scales = NULL; PLFLT *box_line_widths = NULL; //save color Color fakeColor; Color initialColor = changeColor(_pls, fakeColor, _panel->_page->_mode); //set viewport _pls->vpor(_plotAreaBounds._x, _plotAreaBounds._x + _plotAreaBounds._width + legendProp.getLeftOffset(), _plotAreaBounds._y, _plotAreaBounds._y + _plotAreaBounds._height); //set font _pls->schr(getPlFontDef(legendProp.getFont()), getPlFontScaleFactor(legendProp.getFont())); // Set font style. _pls->sfont(getPlFontFamily(legendProp.getFont()), getPlFontStyle(legendProp.getFont()), getPlFontWeight(legendProp.getFont())); //init cmap0 to draw legend std::vector colors = legendProp.getColorsMap(); _pls->scmap0n(colors.size()); index = 0; for (auto color : colors) { _pls->scol0(index,color._red,color._green,color._blue); ++index; } //draw tthe legend _pls->legend( &legend_width, &legend_height, opt, position, x_offset, y_offset, plot_width, bg_color, bb_color, bb_style, 0, /* nrow */ 0, /* ncolumn */ nbLines, opt_array, text_offset, text_scale, text_spacing, text_justification, text_colors, (const char **) texts, box_colors, box_patterns, box_scales, box_line_widths, line_colors, line_styles, line_widths, symbol_colors, symbol_scales, symbol_numbers, (const char **) symbols); // Restore initial color. restoreColor(_pls, initialColor, _panel->_page->_mode); legendProp.setDrawn(true); } /** * @brief Draw text legends. */ void PanelPlotOutput::drawTextLegends(void) { double xmin = _plotAreaBounds._x; double xmax = _plotAreaBounds._x + _plotAreaBounds._width; double ymin = _plotAreaBounds._y; double ymax = _plotAreaBounds._y + _plotAreaBounds._height; bool leftTextLegendFound = false; bool rightTextLegendFound = false; bool topTextLegendFound = false; bool bottomTextLegendFound = false; for (auto textLegend : _panel->_textLegendPropertiesList) { if (textLegend->isDrawn()) continue; // Set color Color initialColor = changeColor(_pls, textLegend->getColor(), _panel->_page->_mode); //set font _pls->schr( getPlFontDef(textLegend->getFont()), getPlFontScaleFactor(textLegend->getFont())); // Set font style. _pls->sfont(getPlFontFamily(textLegend->getFont()), getPlFontStyle(textLegend->getFont()), getPlFontWeight(textLegend->getFont())); // If legend at a given position not already drawn : //- Set viewport depending on legend position // - Retrieve text lines from text legend and draw them // - See calculatePlotArea to know how text legend position are calculated std::vector textLines = textLegend->getTextLines(); double disp = 1; switch (textLegend->getPosition()) { case TextLegendPosition::POS_LEFT : if (leftTextLegendFound == false) { _pls->vpor(xmin - textLegend->getOffset(), xmax, ymin, ymax); for (int i=0; i<(int) textLines.size(); i++) { _pls->mtex ("l", disp, 0.5, 0.5, textLines.at(textLines.size() - 1 - i).c_str()); disp += 1.5; } leftTextLegendFound = true; } break; case TextLegendPosition::POS_RIGHT : if (rightTextLegendFound == false) { _pls->vpor(xmin, xmax + textLegend->getOffset(), ymin, ymax); for (int i=0; i<(int) textLines.size(); i++) { _pls->mtex ("r", disp, 0.5, 0.5, textLines.at(i).c_str()); disp += 1.5; } rightTextLegendFound = true; } break; case TextLegendPosition::POS_TOP : if (topTextLegendFound == false) { _pls->vpor(xmin, xmax, ymin, ymax + textLegend->getOffset()); for (int i=0; i<(int) textLines.size(); i++) { _pls->mtex ("t", disp, 0.5, 0.5, textLines.at(textLines.size() - 1 - i).c_str()); disp += 1.5; } topTextLegendFound = true; } break; case TextLegendPosition::POS_BOTTOM : if (bottomTextLegendFound == false) { _pls->vpor(xmin, xmax, ymin - textLegend->getOffset(), ymax); for (int i=0; i<(int) textLines.size(); i++) { _pls->mtex ("b", disp, 0.5, 0.5, textLines.at(i).c_str()); disp += 1.5; } bottomTextLegendFound = true; } break; default : break; } restoreColor(_pls, initialColor, _panel->_page->_mode); textLegend->setDrawn(true); } } /** * Draws Curve */ void PanelPlotOutput::drawCurvePlot(CurvePlot &/*curvePlot*/) { //Nothing to do - Deleguate to subclasses } /** * @brief Write plot context */ void PanelPlotOutput::writeContext(ContextFileWriter& writer) { writer.startElement("panel"); _panel->writeContext(_pls, writer); if (!_parameterAxesList.empty()) { bool hasSpectro = false; for (auto parameter : _parameterAxesList) { if (parameter.getSpectroProperties() != nullptr) { hasSpectro = true; break; } } writer.startElement("plotArea"); if (_panel->_page->_orientation == PlotCommon::Orientation::PORTRAIT) { writer.addAttribute("x",std::to_string(_plotAreaBounds._y*std::get<0>(_panel->_page->getSize())).c_str()); writer.addAttribute("y",std::to_string(_plotAreaBounds._x*std::get<1>(_panel->_page->getSize())).c_str()); writer.addAttribute("width",std::to_string(_plotAreaBounds._height*std::get<0>(_panel->_page->getSize())).c_str()); writer.addAttribute("height",std::to_string(_plotAreaBounds._width*std::get<1>(_panel->_page->getSize())).c_str()); } else { writer.addAttribute("x",std::to_string(_plotAreaBounds._x*std::get<0>(_panel->_page->getSize())).c_str()); writer.addAttribute("y",std::to_string(_plotAreaBounds._y*std::get<1>(_panel->_page->getSize())).c_str()); writer.addAttribute("width",std::to_string(_plotAreaBounds._width*std::get<0>(_panel->_page->getSize())).c_str()); writer.addAttribute("height",std::to_string(_plotAreaBounds._height*std::get<1>(_panel->_page->getSize())).c_str()); } writer.addAttribute("hasSpectro", hasSpectro ? "true" : "false"); for (Axes::iterator it = _panel->_axes.begin(); it != _panel->_axes.end(); ++it) { boost::shared_ptr lAxis = it->second; if (lAxis == nullptr) continue; if (lAxis->_visible && lAxis->_used) lAxis->writeContext(writer); } writer.endElement(); } writer.endElement(); } /** * @brief Compute the initial plot area for the panel */ void PanelPlotOutput::preparePlotArea(double /*startTime*/, double /*stopTime*/, int /*intervalIndex*/) { _plotAreaBounds = Panel::getCoordinateInPlPage(_pls.get(), _panel->_bounds, _panel->_page); calculatePlotArea (_plotAreaBounds); } /** * @brief Retrieve plot area bounds for the panel */ void PanelPlotOutput::getPlotAreaBounds (Bounds &plotAreaBounds) { plotAreaBounds = _plotAreaBounds; } /** * @brief Force the plot area horizontal position and width */ void PanelPlotOutput::forcePlotAreaPosAndWidth(double plotAreaX, double plotAreaWidth) { _plotAreaBounds._x = plotAreaX; _plotAreaBounds._width = plotAreaWidth; } /** * @brief Retrieve left axis tickMark width */ int PanelPlotOutput::getLeftAxisTickMarkWidth (void) { return _leftAxisTickMarkWidth; } /** * @brief Force left axis tickmark width for the panel */ void PanelPlotOutput::forceLeftAxisTickMarkWidth(int leftAxisTickMarkWidth) { for (Axes::iterator it = _panel->_axes.begin(); it != _panel->_axes.end(); ++it) { boost::shared_ptr lAxis = it->second; if (lAxis == nullptr) continue; // Look for a visible fleft Axis with legend and if ((lAxis->_visible) && (lAxis->_position == PlotCommon::Position::POS_LEFT)) { lAxis->setFixedTickMarkWidth(leftAxisTickMarkWidth); } } } /** * @brief draw the plot for the current time interval */ void PanelPlotOutput::draw(double startTime, double stopTime, int intervalIndex, bool isFirstInterval, bool isLastInterval) { // Sets panel plplot viewport, draw background & title for the panel _panel->draw(_pls); if (_parameterAxesList.empty()) _panel->drawEmptyPanel(_pls); if (isFirstInterval) _panel->_paramsLegendProperties.reset(); //Set pointer to ParameterData list if (_pParameterValues == NULL) { std::stringstream lError; lError << "PanelPlotOutput::draw - Pointer to parameterValues is not set"; BOOST_THROW_EXCEPTION( PanelPlotOutputException() << AMDA::ex_msg(lError.str())); } LOG4CXX_DEBUG(gLogger, "Panel bounds = "<< _panel->_bounds.toString()); LOG4CXX_DEBUG(gLogger, "Plot area = "<< _plotAreaBounds.toString()); //Draw spectro for (auto parameter : _parameterAxesList) { if (parameter.getSpectroProperties() != nullptr) { //draw spectro LOG4CXX_DEBUG(gLogger, "Draw a spectro for parameter " << parameter._originalParamId); // Draw (configure) window for this series. drawSpectro(startTime, stopTime, parameter._originalParamId, *(parameter.getSpectroProperties())); } } // Compute panel XY ratio used for angular conversions computePanelPlotXYRatio(); // Draw fill area between parameter and constant or between parameters drawFills (startTime, stopTime); // Compute nb series to draw by y axis SeriesProperties lSeries; std::map nbSeriesByYAxisMap; for (auto parameter : _parameterAxesList) { std::vector parameterYSerieIndexList = parameter.getYSerieIndexList(_pParameterValues); for (auto lIndex : parameterYSerieIndexList) { // Get series properties lSeries = parameter.getYSeriePropertiesAt(lIndex); if (!lSeries.hasYAxis()) continue; std::string yAxisId = lSeries.getYAxisId(); if (nbSeriesByYAxisMap.find(yAxisId) == nbSeriesByYAxisMap.end()) nbSeriesByYAxisMap[yAxisId] = 0; ++nbSeriesByYAxisMap[yAxisId]; } } // Draw series for parameters. for (auto parameter : _parameterAxesList) { // Get series index to draw for parameter // Draw each index of parameter std::vector parameterYSerieIndexList = parameter.getYSerieIndexList(_pParameterValues); for (auto lIndex : parameterYSerieIndexList) { // Get series properties lSeries = parameter.getYSeriePropertiesAt(lIndex); bool moreThanOneSerieForYAxis = false; if (lSeries.hasYAxis()) moreThanOneSerieForYAxis = (nbSeriesByYAxisMap[lSeries.getYAxisId()] > 1); // Draw (configure) window for this series. drawSeries(startTime, stopTime, intervalIndex, parameter._originalParamId, lSeries, lIndex, parameter, moreThanOneSerieForYAxis); } } // Draw additional objects drawAdditionalObjects(); //Draw parameter legend if (isLastInterval || !_panel->_page->_superposeMode) drawParamsLegend(); //Draw text legends drawTextLegends(); } void PanelPlotOutput::drawSpectro(double /*startDate*/, double /*stopDate*/, std::string /*pParamId*/, SpectroProperties& pSpectro) { // Get X, Y and Z axis. boost::shared_ptr lXAxis(_panel->getAxis(pSpectro.getXAxisId())); boost::shared_ptr lYAxis(_panel->getAxis(pSpectro.getYAxisId())); boost::shared_ptr lZAxis(_panel->getAxis(pSpectro.getZAxisId())); PlWindow lPlWindow; Range lXRange, lYRange, lZRange; if (pSpectro.hasXAxis() && lXAxis.get() == nullptr) { std::stringstream lError; lError << "PanelPlotOutput::drawSpectro" << ": X axis with id '" << pSpectro.getXAxisId() << "' not found."; BOOST_THROW_EXCEPTION( PanelPlotOutputException() << AMDA::ex_msg(lError.str())); } else if(pSpectro.hasXAxis()) { // fill X range (plplot window). lXRange = lXAxis->getRange(); } if (pSpectro.hasYAxis() && lYAxis.get() == nullptr) { std::stringstream lError; lError << "PanelPlotOutput::drawSpectro" << ": Y axis with id '" << pSpectro.getYAxisId() << "' not found."; BOOST_THROW_EXCEPTION( PanelPlotOutputException() << AMDA::ex_msg(lError.str())); lYRange = Range(); } else if(pSpectro.hasYAxis()){ // fill Y range (plplot window). lYRange = lYAxis->getRange(); } if (pSpectro.hasZAxis() && lZAxis.get() == nullptr) { std::stringstream lError; lError << "PanelPlotOutput::drawSpectro" << ": Z axis with id '" << pSpectro.getZAxisId() << "' not found."; BOOST_THROW_EXCEPTION( PanelPlotOutputException() << AMDA::ex_msg(lError.str())); lZRange = Range(); } else if(pSpectro.hasZAxis()){ // fill Z range (plplot window). lZRange = lZAxis->getRange(); } lPlWindow = PlWindow(lXRange.getMin(), lXRange.getMax(), lYRange.getMin(), lYRange.getMax()); // Calculate X and Y tick TickConf lTickConf; double lXMajorTickSpace = nan(""); int lXMinorTickNumber = 0; double lYMajorTickSpace = nan(""); int lYMinorTickNumber = 0; if(pSpectro.hasXAxis()){ // Calculate X tick lXMajorTickSpace = getMajorTickSpace(lXAxis.get(), std::get<0>(lPlWindow), std::get<1>(lPlWindow)); lXMinorTickNumber = getMinorTickNumber(lXAxis.get(), std::get<0>(lPlWindow), std::get<1>(lPlWindow), lXMajorTickSpace); } if(pSpectro.hasYAxis()){ // Calculate Y tick lYMajorTickSpace = getMajorTickSpace(lYAxis.get(), std::get<2>(lPlWindow), std::get<3>(lPlWindow)); lYMinorTickNumber = getMinorTickNumber(lYAxis.get(), std::get<2>(lPlWindow), std::get<3>(lPlWindow), lYMajorTickSpace); } lTickConf = TickConf(lXMajorTickSpace, lXMinorTickNumber, lYMajorTickSpace, lYMinorTickNumber); // Draw Y axis and legend if (pSpectro.hasYAxis() && !lYAxis->_drawn) { drawYAxis(lYAxis, lPlWindow, _plotAreaBounds, lTickConf); // Draw legend. drawLegends(lYAxis, lPlWindow, _plotAreaBounds); } // Draw X axis and legend if (pSpectro.hasXAxis() && !lXAxis->_drawn) { drawXAxis(lXAxis, lPlWindow, _plotAreaBounds, lTickConf); // Draw legend. drawLegends(lXAxis, lPlWindow, _plotAreaBounds); } // Draw Z axis and legend if (pSpectro.hasZAxis() && (lZAxis != nullptr) && !lZAxis->_drawn) { drawZAxis(lZAxis, lPlWindow, _plotAreaBounds, lTickConf); } } void PanelPlotOutput::drawFills(double /*startDate*/, double /*stopDate*/) { // Nothing done here except setting the viewport, see subclasses for specific implementation _pls->vpor( _plotAreaBounds._x, _plotAreaBounds._x + _plotAreaBounds._width, _plotAreaBounds._y, _plotAreaBounds._y + _plotAreaBounds._height); } void PanelPlotOutput::drawSeries(double /*startDate*/, double /*stopDate*/, int /*intervalIndex*/, std::string /*pParamId*/, SeriesProperties& pSeries, AMDA::Common::ParameterIndexComponent /*pParamIndex*/, ParameterAxes& /*param*/, bool /*moreThanOneSerieForAxis*/) { // Get X, Y and Z axis. boost::shared_ptr lXAxis(_panel->getAxis(pSeries.getXAxisId())); boost::shared_ptr lYAxis(_panel->getAxis(pSeries.getYAxisId())); boost::shared_ptr lZAxis(_panel->getAxis(pSeries.getZAxisId())); Range lXRange = getXAxisRange (pSeries, lXAxis); Range lYRange = getYAxisRange (pSeries, lYAxis); //Range lZRange = getZAxisRange (pSeries, lZAxis); PlWindow lPlWindow = PlWindow(lXRange.getMin(), lXRange.getMax(), lYRange.getMin(), lYRange.getMax()); // Calculate X and Y tick TickConf lTickConf; double lXMajorTickSpace = nan(""); int lXMinorTickNumber = 0; double lYMajorTickSpace = nan(""); int lYMinorTickNumber = 0; if(pSeries.hasXAxis()){ // Calculate X tick lXMajorTickSpace = getMajorTickSpace(lXAxis.get(), std::get<0>(lPlWindow), std::get<1>(lPlWindow)); lXMinorTickNumber = getMinorTickNumber(lXAxis.get(), std::get<0>(lPlWindow), std::get<1>(lPlWindow), lXMajorTickSpace); } if(pSeries.hasYAxis()){ // Calculate Y tick lYMajorTickSpace = getMajorTickSpace(lYAxis.get(), std::get<2>(lPlWindow), std::get<3>(lPlWindow)); lYMinorTickNumber = getMinorTickNumber(lYAxis.get(), std::get<2>(lPlWindow), std::get<3>(lPlWindow), lYMajorTickSpace); } lTickConf = TickConf(lXMajorTickSpace, lXMinorTickNumber, lYMajorTickSpace, lYMinorTickNumber); // Draw Y axis and legend if (pSeries.hasYAxis() && !lYAxis->_drawn) { drawYAxis(lYAxis, lPlWindow, _plotAreaBounds, lTickConf); // Draw legend. drawLegends(lYAxis, lPlWindow, _plotAreaBounds); } // Draw X axis and legend if (pSeries.hasXAxis() && !lXAxis->_drawn) { drawXAxis(lXAxis, lPlWindow, _plotAreaBounds, lTickConf); // Draw legend. drawLegends(lXAxis, lPlWindow, _plotAreaBounds); } // Draw Z axis and legend if (pSeries.hasZAxis() && (lZAxis != nullptr) && !lZAxis->_drawn) { drawZAxis(lZAxis, lPlWindow, _plotAreaBounds, lTickConf); } } void PanelPlotOutput::drawAdditionalObjects() { for (auto parameter : _parameterAxesList) { if (parameter.getSpectroProperties() != nullptr) { boost::shared_ptr lXAxis(_panel->getAxis(parameter.getSpectroProperties()->getXAxisId())); boost::shared_ptr lYAxis(_panel->getAxis(parameter.getSpectroProperties()->getYAxisId())); if ((lXAxis == nullptr) || (lYAxis == nullptr)) continue; Range lXRange = lXAxis->getRange(); Range lYRange = lYAxis->getRange(); PlWindow lPlWindow = PlWindow(lXRange.getMin(), lXRange.getMax(), lYRange.getMin(), lYRange.getMax()); if (!lXAxis->_additionalObjDrawn) drawXConstantLines (lXAxis, lPlWindow); if (!lYAxis->_additionalObjDrawn) drawYConstantLines (lYAxis, lPlWindow); if (!lXAxis->_additionalObjDrawn || !lYAxis->_additionalObjDrawn) drawTextPlots (lXAxis, lYAxis, lPlWindow, _panel->_textPlots); lXAxis->_additionalObjDrawn = true; lYAxis->_additionalObjDrawn = true; } } SeriesProperties lSeries; for (auto parameter : _parameterAxesList) { std::vector parameterYSerieIndexList = parameter.getYSerieIndexList(_pParameterValues); for (auto lIndex : parameterYSerieIndexList) { // Get series properties lSeries = parameter.getYSeriePropertiesAt(lIndex); boost::shared_ptr lXAxis(_panel->getAxis(lSeries.getXAxisId())); boost::shared_ptr lYAxis(_panel->getAxis(lSeries.getYAxisId())); if ((lXAxis == nullptr) || (lYAxis == nullptr)) continue; Range lXRange = lXAxis->getRange(); Range lYRange = lYAxis->getRange(); PlWindow lPlWindow = PlWindow(lXRange.getMin(), lXRange.getMax(), lYRange.getMin(), lYRange.getMax()); if (!lXAxis->_additionalObjDrawn) drawXConstantLines (lXAxis, lPlWindow); if (!lYAxis->_additionalObjDrawn) drawYConstantLines (lYAxis, lPlWindow); if (!lXAxis->_additionalObjDrawn || !lYAxis->_additionalObjDrawn) drawTextPlots (lXAxis, lYAxis, lPlWindow, _panel->_textPlots); lXAxis->_additionalObjDrawn = true; lYAxis->_additionalObjDrawn = true; } } // Draw curvePlots for (auto curvePlot : _panel->_curvePlots) drawCurvePlot(*curvePlot); } void PanelPlotOutput::setPlStream(std::shared_ptr& pls) { _pls = pls; } std::string PanelPlotOutput::drawOppositeSide(boost::shared_ptr pAxis) { boost::shared_ptr lNewAxis; std::string lOppositeSide; switch (pAxis->_position) { case PlotCommon::Position::POS_BOTTOM: if (pAxis->_visible && !_plotAreaSideSet[PlotCommon::Position::POS_TOP]) { lOppositeSide = "c"; } break; case PlotCommon::Position::POS_TOP: if (pAxis->_visible && !_plotAreaSideSet[PlotCommon::Position::POS_BOTTOM]) { lOppositeSide = "b"; } break; case PlotCommon::Position::POS_LEFT: if (pAxis->_visible && !_plotAreaSideSet[PlotCommon::Position::POS_RIGHT]) { lOppositeSide = "c"; } break; case PlotCommon::Position::POS_RIGHT: if (pAxis->_visible && !_plotAreaSideSet[PlotCommon::Position::POS_LEFT]) { lOppositeSide = "b"; } break; case PlotCommon::Position::POS_CENTER: default: lOppositeSide = ""; } return lOppositeSide; } /* * Create a sampled parameter from an original parameter and a sampling value */ AMDA::Parameters::ParameterSPtr PanelPlotOutput::createSampledParameter(AMDA::Parameters::ParameterSPtr& originalParam, float samplingValue) { AMDA::Parameters::ParameterSPtr sampledParam = _parameterManager.getSampledParameter( originalParam->getId(), "classic", samplingValue, originalParam->getGapThreshold()); if (sampledParam == NULL) { LOG4CXX_ERROR(gLogger, "ParamOutput::createSampledParameter : cannot create sampled parameter"); BOOST_THROW_EXCEPTION( AMDA::Parameters::ParamOutput_exception()); } LOG4CXX_INFO(gLogger, "ParamOutput::createSampledParameter : sampled parameter : " << sampledParam->getId() << " created"); return sampledParam; } /* * Create a sampled parameter from an original parameter and a reference parameter for time definition */ AMDA::Parameters::ParameterSPtr PanelPlotOutput::createSampledParameterUnderReferenceParameter(AMDA::Parameters::ParameterSPtr& originalParam, AMDA::Parameters::ParameterSPtr& refParam) { AMDA::Parameters::ParameterSPtr sampledParam = _parameterManager.getSampledParameterUnderRefParam( originalParam->getId(), refParam->getId() ); if (sampledParam == NULL) { LOG4CXX_ERROR(gLogger, "ParamOutput::createSampledParameterUnderReferenceParameter : cannot create sampled parameter"); BOOST_THROW_EXCEPTION( AMDA::Parameters::ParamOutput_exception()); } LOG4CXX_INFO(gLogger, "ParamOutput::createSampledParameterUnderReferenceParameter : sampled parameter : " << sampledParam->getId() << " created"); return sampledParam; } ParameterAxes* PanelPlotOutput::getParameterAxesByColorSerieId(int colorSerieId) { if (colorSerieId < 0) return NULL; for (ParameterAxesList::iterator it = _parameterAxesList.begin(); it != _parameterAxesList.end(); ++it) { for (auto serieProp : it->getColorSeriePropertiesList()) { if (serieProp.getId() == colorSerieId) return &(*it); } } LOG4CXX_ERROR(gLogger, "ParamOutput::getColorSeriePropertiesById : Not founded : " << colorSerieId); return NULL; } /** * Create parameters needed for this plot. * By default, the creation method create parameters for a time serie. * Override it for other plot type */ void PanelPlotOutput::createParameters(std::list& usedParametersId_) { // -- for each y serie on each parameter, calculate // y serie sampling according to max resolution for (ParameterAxesList::iterator it = _parameterAxesList.begin(); it != _parameterAxesList.end(); ++it) { AMDA::Parameters::ParameterSPtr originalParam = _parameterManager.getParameter(it->_originalParamId); //original parameter sampling double samplingValue = getSamplingInTreeParameter(originalParam); //For each series std::map::iterator ity; for (ity = it->getYSeriePropertiesMap().begin(); ity != it->getYSeriePropertiesMap().end(); ++ity) { ParameterAxes* colorSerieParameterAxes = getParameterAxesByColorSerieId(ity->second.getColorSerieId()); AMDA::Parameters::ParameterSPtr originalColorParam; if (colorSerieParameterAxes != NULL) originalColorParam = _parameterManager.getParameter(colorSerieParameterAxes->_originalParamId); //get corrected sampling value in relation with max resolution double correctedSamplingValue = getCorrectedSamplingValue(ity->second.getMaxResolution(), samplingValue); AMDA::Parameters::ParameterSPtr usedParam; //create parameter and link to the serie switch (ity->second.getResamplingProperties().getType()) { case ResamplingType::MANUAL : { //create resampling parameters for param usedParam = createSampledParameter(originalParam, ity->second.getResamplingProperties().getValue()); break; } case ResamplingType::AUTO : case ResamplingType::YPARAM : case ResamplingType::XPARAM : if (abs(samplingValue - correctedSamplingValue) > 1.) { //more than one second between samplingValue and correctedSamplingValue //=> use resampling parameter usedParam = createSampledParameter(originalParam, correctedSamplingValue); } else { //use original parameter usedParam = originalParam; } break; } //Add used parameter to parameters list if (std::find (usedParametersId_.begin(),usedParametersId_.end(),usedParam->getId()) == usedParametersId_.end()) usedParametersId_.push_back(usedParam->getId()); //link this paramter to the serie ity->second.setParamId(usedParam->getId()); ErrorBarProperties &errorBarProp = ity->second.getErrorBarProperties(); // Compute min / max re-sampled parameters if min/max error bar are defined for the serie if (errorBarProp.getErrorMinMax() != nullptr) { std::stringstream usedParamIndex; if (ity->second.getIndex().getDim1Index() != -1) { usedParamIndex << "[" << std::to_string(ity->second.getIndex().getDim1Index()) ; if (ity->second.getIndex().getDim2Index() != -1) usedParamIndex << "," << std::to_string(ity->second.getIndex().getDim2Index()); usedParamIndex << "]"; } std::stringstream minParamIndex; if (errorBarProp.getErrorMinMax()->getIndexMin().getDim1Index() != -1) { minParamIndex << "[" << std::to_string(errorBarProp.getErrorMinMax()->getIndexMin().getDim1Index()); if (errorBarProp.getErrorMinMax()->getIndexMin().getDim2Index() != -1) minParamIndex << "," << std::to_string(errorBarProp.getErrorMinMax()->getIndexMin().getDim2Index()); minParamIndex << "]"; } std::stringstream maxParamIndex; if (errorBarProp.getErrorMinMax()->getIndexMax().getDim1Index() != -1) { maxParamIndex << "[" << std::to_string(errorBarProp.getErrorMinMax()->getIndexMax().getDim1Index()); if (errorBarProp.getErrorMinMax()->getIndexMax().getDim2Index() != -1) maxParamIndex << "," << std::to_string(errorBarProp.getErrorMinMax()->getIndexMax().getDim2Index()); maxParamIndex << "]"; } // Build expression for computed parameter = usedParam - minParam std::stringstream minExpr; minExpr << "$" << usedParam->getId() << usedParamIndex.str() << "-$" + errorBarProp.getErrorMinMax()->getOriginalParamMin() << minParamIndex.str(); //create parameter from expression AMDA::Parameters::ParameterSPtr usedMinParam = _parameterManager.getParameterFromExpression(minExpr.str()); if (usedMinParam == nullptr) { LOG4CXX_ERROR(gLogger, "PanelPlotOutput::createParameters - Cannot create parameter from expression " << minExpr); continue; } if (std::find (usedParametersId_.begin(),usedParametersId_.end(),usedMinParam->getId()) == usedParametersId_.end()) usedParametersId_.push_back(usedMinParam->getId()); errorBarProp.getErrorMinMax()->setUsedParamMin(usedMinParam->getId()); // Build expression for computed parameter = usedParam + maxParam std::stringstream maxExpr; maxExpr << "$" << usedParam->getId() << usedParamIndex.str() << "+$" + errorBarProp.getErrorMinMax()->getOriginalParamMax() << maxParamIndex.str(); //create parameter from expression AMDA::Parameters::ParameterSPtr usedMaxParam = _parameterManager.getParameterFromExpression(maxExpr.str()); if (usedMaxParam == nullptr) { LOG4CXX_ERROR(gLogger, "PanelPlotOutput::createParameters - Cannot create parameter from expression " << maxExpr); continue; } if (std::find (usedParametersId_.begin(),usedParametersId_.end(),usedMaxParam->getId()) == usedParametersId_.end()) usedParametersId_.push_back(usedMaxParam->getId()); errorBarProp.getErrorMinMax()->setUsedParamMax(usedMaxParam->getId()); } if (originalColorParam != nullptr) { AMDA::Parameters::ParameterSPtr usedColorParam = createSampledParameterUnderReferenceParameter(originalColorParam, usedParam); //Add used color parameter to parameters list if (std::find (usedParametersId_.begin(),usedParametersId_.end(),usedColorParam->getId()) == usedParametersId_.end()) usedParametersId_.push_back(usedColorParam->getId()); //link this color parameter to the y serie ity->second.setColorParamId(usedColorParam->getId()); //link the used parameter to the color serie colorSerieParameterAxes->getColorSeriePropertiesById(ity->second.getColorSerieId()).addParamId(usedParam->getId(), usedColorParam->getId()); //activate the Z Axis ity->second.setZAxis(true); } } //For spectro if defined std::shared_ptr pSpecProp = it->getSpectroProperties(); if (pSpecProp != nullptr) { AMDA::Info::ParamInfoSPtr paramInfo = AMDA::Info::ParamMgr::getInstance()->getParamInfoFromId(originalParam->getInfoId()); boost::shared_ptr tableSPtr; if (paramInfo != nullptr) tableSPtr = paramInfo->getTable(pSpecProp->getRelatedDim()); //get corrected sampling value in relation with max resolution double correctedSamplingValue = getCorrectedSamplingValue(pSpecProp->getMaxResolution(), samplingValue); AMDA::Parameters::ParameterSPtr usedParam; if (abs(samplingValue - correctedSamplingValue) > 1.) { //more than one second between samplingValue and correctedSamplingValue //=> use resampling parameter usedParam = createSampledParameter(originalParam, correctedSamplingValue); if ((tableSPtr != nullptr) && tableSPtr->isVariable()) { for (std::map::iterator it = tableSPtr->getTableParams().begin(); it != tableSPtr->getTableParams().end(); ++it) { std::string tableParamKey = it->first; std::string tableParamName = it->second; AMDA::Parameters::ParameterSPtr originalTableParam = _parameterManager.getParameter(tableParamName); AMDA::Parameters::ParameterSPtr usedTableParam = createSampledParameter(originalTableParam, correctedSamplingValue); pSpecProp->addTableParam(tableParamKey, usedTableParam->getId()); } } } else { //use original parameter usedParam = originalParam; if ((tableSPtr != nullptr) && tableSPtr->isVariable()) { for (std::map::iterator it = tableSPtr->getTableParams().begin(); it != tableSPtr->getTableParams().end(); ++it) { std::string tableParamKey = it->first; std::string tableParamName = it->second; AMDA::Parameters::ParameterSPtr originalTableParam = _parameterManager.getParameter(tableParamName); pSpecProp->addTableParam(tableParamKey, originalTableParam->getId()); } } } //Add used parameter to parameters list if (std::find (usedParametersId_.begin(),usedParametersId_.end(),usedParam->getId()) == usedParametersId_.end()) usedParametersId_.push_back(usedParam->getId()); pSpecProp->setParamId(usedParam->getId()); //Add table parameters to parameters list for (std::map::iterator it = pSpecProp->getTableParams().begin(); it != pSpecProp->getTableParams().end(); ++it) { std::string tableParamKey = it->first; std::string tableParamId = it->second; if (std::find (usedParametersId_.begin(),usedParametersId_.end(),tableParamId) == usedParametersId_.end()) usedParametersId_.push_back(tableParamId); } } } } /** * Gets sampling value from resolution (point-per-plot) according to * the plot time interval and the base parameter sampling value. */ double PanelPlotOutput::getCorrectedSamplingValue(int maxResolution, double samplingValue){ double timeInt = 0.; if (_panel->_page->_superposeMode) { //merge all intervals size TimeIntervalList::iterator crtTimeInterval = _parameterManager.getInputIntervals()->begin(); while (crtTimeInterval != _parameterManager.getInputIntervals()->end()) { timeInt += (crtTimeInterval->_stopTime - crtTimeInterval->_startTime); ++crtTimeInterval; } } else { //find the biggest intervals size TimeIntervalList::iterator crtTimeInterval = _parameterManager.getInputIntervals()->begin(); while (crtTimeInterval != _parameterManager.getInputIntervals()->end()) { if ((crtTimeInterval->_stopTime - crtTimeInterval->_startTime) > timeInt) timeInt = crtTimeInterval->_stopTime - crtTimeInterval->_startTime; ++crtTimeInterval; } } if((timeInt == 0) || (maxResolution == -1) || (timeInt/samplingValue < maxResolution)){ return samplingValue; } // get the exact sampling double correctedSampling = timeInt/maxResolution; // rounded it to the next int sampling double roundedSampling = (int) correctedSampling; if(roundedSampling < correctedSampling){ roundedSampling += 1; } return roundedSampling; } /** * Gets indexes of series with no color definition on a given axis. */ int PanelPlotOutput::countAxisSerieWithUndefinedColor(const std::string& axisId_) { int numberOfSeries = 0; for (ParameterAxesList::iterator paramAxeIt = _parameterAxesList.begin(); paramAxeIt != _parameterAxesList.end(); ++paramAxeIt) { try { // browse all real index (not serie index that may contain -1) for (auto index : paramAxeIt->getYSerieIndexList(_pParameterValues)) { SeriesProperties serie = paramAxeIt->getYSeriePropertiesAt( index); if (serie.getYAxisId() == axisId_ && !serie.getColor().isSet()) { ++numberOfSeries; } } } catch (PanelPlotOutputException& ex) { // nop } } return numberOfSeries; } /** * @brief Get the list of indexes used for a parameter */ std::vector PanelPlotOutput::getParamUsedIndexes(std::string paramId, int dim1Size, int dim2Size) { std::vector indexes; for (ParameterAxesList::iterator paramAxeIt = _parameterAxesList.begin(); paramAxeIt != _parameterAxesList.end(); ++paramAxeIt) { //get indexes for spectro std::shared_ptr pSpecProp = paramAxeIt->getSpectroProperties(); if (pSpecProp != nullptr) { if (pSpecProp->getParamId() == paramId) { if (pSpecProp->getIndexes().empty()) { //get used indexes by the spectro thanks to the index definition in the request AMDA::Common::ParameterIndexComponentList indexList; AMDA::Common::ParameterIndexesTool::parse(pSpecProp->getIndexDef(), dim1Size, dim2Size, indexList); pSpecProp->setIndexes(indexList); } for (auto index : pSpecProp->getIndexes()) { if (std::find(indexes.begin(),indexes.end(),index) != indexes.end()) continue; indexes.push_back(index); } } } //get indexes for series of this ParameterAxe std::vector seriesIndexes = paramAxeIt->getParamUsedIndexes(paramId); for (AMDA::Common::ParameterIndexComponent index : seriesIndexes) { if ((index.getDim1Index() == -1) && (index.getDim2Index() == -1)) { //-1 => all indexes indexes.clear(); indexes.push_back(AMDA::Common::ParameterIndexComponent(-1,-1)); return indexes; } if (std::find(indexes.begin(),indexes.end(),index) != indexes.end()) continue; indexes.push_back(index); } } if (indexes.empty()) indexes.push_back(AMDA::Common::ParameterIndexComponent(-1,-1)); return indexes; } /* * @brief Set pointer to params values */ void PanelPlotOutput::setParameterValues(std::map *pParameterValues) { _pParameterValues = pParameterValues; } /* * @brief Set a pointer to the time intervals list */ void PanelPlotOutput::setTimeIntervalListPtr(AMDA::Parameters::TimeIntervalList* timeIntervalListPtr) { _timeIntervalListPtr = timeIntervalListPtr; } /* * @brief Get computed values (in relation with the y axis definition) for a y serie and a time interval * Do not forget to delete computedValues !! * Don't delete timeValues !! */ bool PanelPlotOutput::getComputedValuesFromSerieAndInterval(double startDate, double stopDate, SeriesProperties &rSeriesProperties, AMDA::Common::ParameterIndexComponent index, double** computedValues, double** timeValues, int& nbValues) { LOG4CXX_DEBUG(gLogger, "PanelPlotOutput::getComputedValuesFromSerieAndInterval"); //get parameter data for this serie ParameterData &data = (*_pParameterValues)[rSeriesProperties.getParamId()]; //get original data for interval [startDate, stopDate] int startIndex; double *valuesInterval = data.getIntervalValues(startDate, stopDate, index, startIndex, nbValues); if (valuesInterval == NULL) { LOG4CXX_DEBUG(gLogger, "PanelPlotOutput::getComputedValuesFromSerieAndInterval - Cannot find data for serie with id " << rSeriesProperties.getId()); return false; } //get computed data for interval [startDate, stopDate] in relation with the serie y axis (*computedValues) = _panel->getAxis(rSeriesProperties.getYAxisId())->getComputedValues( valuesInterval, nbValues, rSeriesProperties.getMin(), rSeriesProperties.getMax()); //get time values (*timeValues) = &data.getTimes()[startIndex]; return true; } /* * @brief Get computed values (in relation with the color axis definition) for a color serie and a time interval * Do not forget to delete computedValues !! * Don't delete timeValues !! */ bool PanelPlotOutput::getColoredComputedValuesFromSerieAndInterval(double startDate, double stopDate, SeriesProperties &rSeriesProperties, double** computedValues, double** timeValues, int& nbValues) { LOG4CXX_DEBUG(gLogger, "PanelPlotOutput::getColoredComputedValuesFromSerieAndInterval"); //get parameter data for this color serie ParameterData &data = (*_pParameterValues)[rSeriesProperties.getColorParamId()]; ParameterAxes* colorSerieParameterAxes = getParameterAxesByColorSerieId(rSeriesProperties.getColorSerieId()); if (colorSerieParameterAxes == NULL) { LOG4CXX_DEBUG(gLogger, "PanelPlotOutput::getColoredComputedValuesFromSerieAndInterval - Cannot retrieve parameter axe"); return false; } ColorSeriesProperties& colorSerieProp = colorSerieParameterAxes->getColorSeriePropertiesById(rSeriesProperties.getColorSerieId()); //get original data for interval [startDate, stopDate] int startIndex; double *valuesInterval = data.getIntervalValues(startDate, stopDate, colorSerieProp.getIndex(), startIndex, nbValues); if (valuesInterval == NULL) { LOG4CXX_DEBUG(gLogger, "PanelPlotOutput::getColoredComputedValuesFromSerieAndInterval - Cannot find data for color serie with id " << colorSerieProp.getId()); return false; } //get computed data for interval [startDate, stopDate] in relation with the color axis (*computedValues) = _panel->getColorAxis()->getComputedValues( valuesInterval, nbValues, colorSerieProp.getMin(), colorSerieProp.getMax()); //get time values (*timeValues) = &data.getTimes()[startIndex]; return true; } /* * @brief Get computed values (in relation with the y axis definition) for a y serie and a time interval * Do not forget to delete computedValues !! * Don't delete timeValues !! */ bool PanelPlotOutput::getErrorComputedValuesFromSerieAndInterval(double startDate, double stopDate, SeriesProperties &rSeriesProperties, double** minComputedValues, double** minTimeValues, int& nbMinValues, double** maxComputedValues, double** maxTimeValues, int& nbMaxValues) { LOG4CXX_DEBUG(gLogger, "PanelPlotOutput::getErrorComputedValuesFromSrieAndInterval"); ErrorBarProperties &errorBarProp = rSeriesProperties.getErrorBarProperties(); if (errorBarProp.getErrorMinMax() == nullptr) return false; //get parameter dataMin for this color bar ParameterData &dataMin = (*_pParameterValues)[errorBarProp.getErrorMinMax()->getUsedParamMin()]; //get original min data for interval [startDate, stopDate] int minStartIndex; double *minValuesInterval = dataMin.getIntervalValues(startDate, stopDate, errorBarProp.getErrorMinMax()->getIndexMin(), minStartIndex, nbMinValues); if (minValuesInterval == NULL) { LOG4CXX_DEBUG(gLogger, "PanelPlotOutput::getErrorComputedValuesFromSerieAndInterval - Cannot get min data for error bar"); return false; } //get computed min data for interval [startDate, stopDate] in relation with the serie y axis (*minComputedValues) = _panel->getAxis(rSeriesProperties.getYAxisId())->getComputedValues( minValuesInterval, nbMinValues, rSeriesProperties.getMin(), rSeriesProperties.getMax()); //get time values (*minTimeValues) = &dataMin.getTimes()[minStartIndex]; //get parameter dataMax for this color bar ParameterData &dataMax = (*_pParameterValues)[errorBarProp.getErrorMinMax()->getUsedParamMax()]; //get original max data for interval [startDate, stopDate] int maxStartIndex; double *maxValuesInterval = dataMax.getIntervalValues(startDate, stopDate, errorBarProp.getErrorMinMax()->getIndexMax(), maxStartIndex, nbMaxValues); if (maxValuesInterval == NULL) { LOG4CXX_DEBUG(gLogger, "PanelPlotOutput::getErrorComputedValuesFromSerieAndInterval - Cannot get min data for error bar"); delete[] (*minComputedValues); return false; } //get computed max data for interval [startDate, stopDate] in relation with the serie y axis (*maxComputedValues) = _panel->getAxis(rSeriesProperties.getYAxisId())->getComputedValues( maxValuesInterval, nbMaxValues, rSeriesProperties.getMin(), rSeriesProperties.getMax()); //get time values (*maxTimeValues) = &dataMax.getTimes()[maxStartIndex]; return true; } /* * @brief Return the color to draw the line of a serie */ Color PanelPlotOutput::getSerieLineColor(SeriesProperties &rSeriesProperties, bool moreThanOneSerieForAxis) { Color lLineColor = rSeriesProperties.getLineProperties().getColor(); if((lLineColor._colorIndex == -1) && (lLineColor._red == -1) && (lLineColor._green == -1) && (lLineColor._blue == -1)) { //if line color not defined, try to use the serie color lLineColor = rSeriesProperties.getColor(); if((lLineColor._colorIndex == -1) && (lLineColor._red == -1) && (lLineColor._green == -1) && (lLineColor._blue == -1)) { //if color not defined if (!_panel->_page->_superposeMode && !moreThanOneSerieForAxis) { lLineColor = _panel->getAxis(rSeriesProperties.getYAxisId())->_color; } else { //get a default color getDefaultColor(_panel->_page->_mode, _automaticSerieColorCursor, lLineColor); } } } return lLineColor; } /* * @brief Return the color to draw the symbols of a serie */ Color PanelPlotOutput::getSerieSymbolColor(SeriesProperties &rSeriesProperties, Color &pLineColor) { Color lSymbolColor = rSeriesProperties.getSymbolProperties().getColor(); if((lSymbolColor._colorIndex == -1) && (lSymbolColor._red == -1) && (lSymbolColor._green == -1) && (lSymbolColor._blue == -1)) { //if not defined, use the line color lSymbolColor = pLineColor; } return lSymbolColor; } /** * @brief Configure params legend. */ void PanelPlotOutput::configureParamsLegend(double startTime, double stopTime, int intervalIndex) { _panel->_paramsLegendProperties.reset(); for (auto parameter : _parameterAxesList) { for (auto lIndex : parameter.getYSerieIndexList(_pParameterValues)) { SeriesProperties lSeriesProperties = parameter.getYSeriePropertiesAt(lIndex); //for the legend configuration, we don't need to know the line and symbol colors Color fakeColor(0,0,0); addSerieToParamsLegend(lSeriesProperties,lIndex, parameter._originalParamId, fakeColor, fakeColor, startTime, stopTime, intervalIndex); } } } /** * @brief Add a serie to the param legend */ void PanelPlotOutput::addSerieToParamsLegend(SeriesProperties& lSeriesProperties, AMDA::Common::ParameterIndexComponent& index, std::string originalParamId, Color& lineColor, Color& symbolColor, double startTime, double stopTime, int intervalIndex) { if (!_panel->_paramsLegendProperties.isVisible()) return; LOG4CXX_DEBUG(gLogger, "PanelPlotOutput::addSerieToParamsLegend"); std::stringstream fullLegendText; if (_panel->_paramsLegendProperties.isParamInfoVisible()) { //add params info fullLegendText << getSerieParamsLegendString(lSeriesProperties,index,originalParamId); } if (_panel->_paramsLegendProperties.isIntervalInfoVisible()) { //add interval info if (!fullLegendText.str().empty()) fullLegendText << "; "; switch (_panel->_paramsLegendProperties.getIntervalInfoType()) { case ParamsLegendProperties::IntervalInfoType::INDEX : fullLegendText << "I"; fullLegendText << intervalIndex; break; case ParamsLegendProperties::IntervalInfoType::STARTSTOP : fullLegendText << "["; fullLegendText << AMDA::TimeUtil::formatTimeDateInIso(startTime); fullLegendText << ", "; fullLegendText << AMDA::TimeUtil::formatTimeDateInIso(stopTime); fullLegendText << "]"; } } //push this legend to the params legend properties _panel->_paramsLegendProperties.addSerie(lSeriesProperties, fullLegendText.str().c_str(), lineColor, symbolColor); } /* * @brief Return the associated params legend to a serie */ std::string PanelPlotOutput::getSerieParamsLegendString(SeriesProperties &rSeriesProperties, AMDA::Common::ParameterIndexComponent& index, std::string originalParamId) { // Retrieve ParamInfo Manager ParamMgr *piMgr =ParamMgr::getInstance(); // Try to retrieve informations from paramInfo ParameterSPtr p = _parameterManager.getParameter(rSeriesProperties.getParamId()); ParamInfoSPtr paramInfo = piMgr->getParamInfoFromId(p->getInfoId()); // Build parameter text legend depending on the availability of paramInfo std::stringstream paramLegendText; if (paramInfo) { if ((index.getDim1Index() == -1) && (index.getDim2Index() == -1)) { // parameter legend text = short_name paramLegendText << paramInfo->getShortName(); } else { if (paramInfo->getComponents(index).empty() == false) // parameter legend text = components at index lIndex] paramLegendText << paramInfo->getComponents(index); else { // parameter legend text = short_name [lIndex] paramLegendText << paramInfo->getShortName() << "[" << index.getDim1Index(); if (index.getDim2Index() != -1) paramLegendText << "," << index.getDim2Index(); paramLegendText << "]"; } } } else { if ((index.getDim1Index() == -1) && (index.getDim2Index() == -1)) // parameter legend text = _originalParamId paramLegendText << originalParamId; else { // parameter legend text = _originalParamId [lIndex] paramLegendText << originalParamId << "[" << index.getDim1Index(); if (index.getDim2Index() != -1) paramLegendText << "," << index.getDim2Index(); paramLegendText << "]"; } } return paramLegendText.str(); } /* * Dumps properties for test. */ void PanelPlotOutput::dump(std::ostream& out) { out << *(_panel->_page) << std::endl; out << *_panel << std::endl; std::string prefix = "parameter."; for (auto parameter : _parameterAxesList) { parameter.dump(out, prefix); } } }/* namespace plot */