/* * XYPlot.cc * * Created on: 29 oct. 2013 * Author: CS */ #include #include #include #include #include "XYPlot.hh" #include "ParamsNode.hh" #include "AxesNode.hh" #include "PlotLogger.hh" #include "PlotOutput.hh" #include "TimeUtil.hh" #include "ParamMgr.hh" #include "AxisLegendManager.hh" using namespace AMDA::Parameters; using namespace AMDA::Info; namespace plot { XYPlot::XYPlot(AMDA::Parameters::ParameterManager& manager, boost::shared_ptr panel) : PanelPlotOutput(manager, panel), _isIsotropic(false) { } XYPlot::~XYPlot() { } /** * @overload PanelPlotOutput::preparePlotArea() */ void XYPlot::preparePlotArea(double startTime, double stopTime, int intervalIndex) { // for test, dump plot properties const char* lBuildType=getenv("BUILD_TYPE"); if(lBuildType && std::string(lBuildType) == "Debug") { std::ofstream out("xyPlot.txt"); dump(out); out.close(); } // Configure range of series and color. configureSeriesAxis(); configureAxisLegend(); // Configure params legend configureParamsLegend(startTime,stopTime,intervalIndex); // If panel title is empty, replace it with start and end date. if (_panel->getTitle()->_text.empty() || _panel->_updateTitleOnNextInterval) { //Title must be updated during the next interval plot _panel->_updateTitleOnNextInterval = true; // Set start date and end date. std::string lTimeFormat("%H:%M %d/%m/%Y"); long int lStartTime = static_cast(startTime); tm * lStartTimeTm = gmtime(&lStartTime); char lStartTimeChr[80]; // Format date. strftime(lStartTimeChr, 80, lTimeFormat.c_str(), lStartTimeTm); long int lStopTime = static_cast(stopTime); tm * lStopTimeTm = gmtime(&lStopTime); char lStopTimeChr[80]; // Format date. strftime(lStopTimeChr, 80, lTimeFormat.c_str(), lStopTimeTm); std::string titleText = std::string(lStartTimeChr) + " - " + std::string(lStopTimeChr); _panel->setTitleText(titleText.c_str()); } PanelPlotOutput::preparePlotArea(startTime,stopTime,intervalIndex); } void XYPlot::createParameters(std::list& usedParametersId_) { for (ParameterAxesList::iterator it = _parameterAxesList.begin(); it != _parameterAxesList.end(); ++it) { std::vector::iterator ity; AMDA::Parameters::ParameterSPtr originalYParam = _parameterManager.getParameter(it->_originalParamId); double samplingYValue = 0.; double samplingXValue = 0.; for (ity = it->getYSeriePropertiesList().begin(); ity != it->getYSeriePropertiesList().end(); ++ity) { ParameterAxes* xSerieParameterAxes = getParameterAxesByXSerieId(ity->getXId()); if (xSerieParameterAxes == NULL) { continue; } XSeriesProperties& xSerie = xSerieParameterAxes->getXSeriePropertiesById(ity->getXId()); AMDA::Parameters::ParameterSPtr originalXSerieParam = _parameterManager.getParameter(xSerieParameterAxes->_originalParamId); ParameterAxes* colorSerieParameterAxes = getParameterAxesByColorSerieId(ity->getColorSerieId()); AMDA::Parameters::ParameterSPtr originalColorParam; if (colorSerieParameterAxes != NULL) originalColorParam = _parameterManager.getParameter(colorSerieParameterAxes->_originalParamId); AMDA::Parameters::ParameterSPtr usedXParam; AMDA::Parameters::ParameterSPtr usedYParam; if (!ity->getComputeExpression().empty()) { if (samplingXValue == 0.) samplingXValue = getSamplingInTreeParameter(originalXSerieParam); double correctedSamplingValue = getCorrectedSamplingValue(ity->getMaxResolution(), samplingXValue); if (abs(samplingXValue - correctedSamplingValue) > 1.) { //create resampling parameter for xparam usedXParam = createSampledParameter(originalXSerieParam, correctedSamplingValue); } else { usedXParam = originalXSerieParam; } if (std::find (usedParametersId_.begin(),usedParametersId_.end(),usedXParam->getId()) == usedParametersId_.end()) usedParametersId_.push_back(usedXParam->getId()); std::string expr = ity->getComputeExpression(); boost::replace_all(expr, OrbitParamComponentName, usedXParam->getId()); //create parameter from expression usedYParam = _parameterManager.getParameterFromExpression(expr, usedXParam->getGapThreshold(), true); if (usedYParam == nullptr) { LOG4CXX_ERROR(gLogger, "XYPlot::createParameters - Cannot create parameter from expression " << expr); continue; } AMDA::Info::ParamInfoSPtr paramYInfo = AMDA::Info::ParamMgr::getInstance()->getParamInfoFromId(usedYParam->getId(),true); AMDA::Info::ParamInfoSPtr paramXInfo = AMDA::Info::ParamMgr::getInstance()->getParamInfoFromId(originalXSerieParam->getInfoId(),true); if (paramYInfo != nullptr) { paramYInfo->setShortName(ity->getComputeExpressionName()); if (paramXInfo != nullptr) { paramYInfo->setUnits(paramXInfo->getUnits()); paramYInfo->setCoordinatesSystem(paramXInfo->getCoordinatesSystem()); } } if (std::find (usedParametersId_.begin(),usedParametersId_.end(),usedYParam->getId()) == usedParametersId_.end()) usedParametersId_.push_back(usedYParam->getId()); //link serie to this resampled parameter ity->setParamId(usedYParam->getId()); xSerie.setParamId(usedXParam->getId()); if (originalColorParam != nullptr) { AMDA::Parameters::ParameterSPtr usedColorParam = createSampledParameterUnderReferenceParameter(originalColorParam, usedYParam); //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->setColorParamId(usedColorParam->getId()); //link the used parameter to the color serie colorSerieParameterAxes->getColorSeriePropertiesById(ity->getColorSerieId()).addParamId(usedYParam->getId(), usedColorParam->getId()); //activate the Z Axis ity->setZAxis(true); } usedYParam.reset(); continue; } if (samplingYValue == 0.) samplingYValue = getSamplingInTreeParameter(originalYParam); samplingXValue = getSamplingInTreeParameter(originalXSerieParam); switch (ity->getResamplingProperties().getType()) { case ResamplingType::MANUAL : { LOG4CXX_DEBUG(gLogger, "XYPlot::createSeriesParameters - ResamplingType::MANUAL"); //create resampling parameters for xparam usedXParam = createSampledParameter(originalXSerieParam, ity->getResamplingProperties().getValue()); if (std::find (usedParametersId_.begin(),usedParametersId_.end(),usedXParam->getId()) == usedParametersId_.end()) usedParametersId_.push_back(usedXParam->getId()); if (originalXSerieParam == originalYParam) { //same original parameter => re-use xparam usedYParam = usedXParam; } else { //create resampling parameters for xparam usedYParam = createSampledParameter(originalYParam, ity->getResamplingProperties().getValue()); if (std::find (usedParametersId_.begin(),usedParametersId_.end(),usedYParam->getId()) == usedParametersId_.end()) usedParametersId_.push_back(usedYParam->getId()); } } break; case ResamplingType::AUTO : case ResamplingType::XPARAM : { LOG4CXX_DEBUG(gLogger, "XYPlot::createSeriesParameters - ResamplingType::XPARAM"); double correctedSamplingValue = getCorrectedSamplingValue(ity->getMaxResolution(), samplingXValue); if (abs(samplingXValue - correctedSamplingValue) > 1.) { //create resampling parameter for xparam usedXParam = createSampledParameter(originalXSerieParam, correctedSamplingValue); if (std::find (usedParametersId_.begin(),usedParametersId_.end(),usedXParam->getId()) == usedParametersId_.end()) usedParametersId_.push_back(usedXParam->getId()); if (originalXSerieParam == originalYParam) { //same original parameter => re-use xparam usedYParam = usedXParam; } else { //create resampling parameter for yparam usedYParam = createSampledParameter(originalYParam, correctedSamplingValue); if (std::find (usedParametersId_.begin(),usedParametersId_.end(),usedYParam->getId()) == usedParametersId_.end()) usedParametersId_.push_back(usedYParam->getId()); } } else { //get original parameter for xparam usedXParam = originalXSerieParam; if (std::find (usedParametersId_.begin(),usedParametersId_.end(),usedXParam->getId()) == usedParametersId_.end()) usedParametersId_.push_back(usedXParam->getId()); if (originalXSerieParam == originalYParam) { //same original parameter => re-use xparam usedYParam = usedXParam; } else { //create resampled parameter under times of xparam for yparam usedYParam = createSampledParameterUnderReferenceParameter(originalYParam, originalXSerieParam); if (std::find (usedParametersId_.begin(),usedParametersId_.end(),usedYParam->getId()) == usedParametersId_.end()) usedParametersId_.push_back(usedYParam->getId()); } } } break; case ResamplingType::YPARAM : LOG4CXX_DEBUG(gLogger, "XYPlot::createSeriesParameters - ResamplingType::YPARAM"); double correctedSamplingValue = getCorrectedSamplingValue(ity->getMaxResolution(), samplingYValue); if (abs(samplingYValue - correctedSamplingValue) > 1.) { //create resampling parameter for xparam usedXParam = createSampledParameter(originalXSerieParam, correctedSamplingValue); if (std::find (usedParametersId_.begin(),usedParametersId_.end(),usedXParam->getId()) == usedParametersId_.end()) usedParametersId_.push_back(usedXParam->getId()); if (originalXSerieParam == originalYParam) { //same original parameter => re-use xparam usedYParam = usedXParam; } else { //create resampling parameter for yparam usedYParam = createSampledParameter(originalYParam, correctedSamplingValue); if (std::find (usedParametersId_.begin(),usedParametersId_.end(),usedYParam->getId()) == usedParametersId_.end()) usedParametersId_.push_back(usedYParam->getId()); } } else { //get original parameter for yparam usedYParam = originalYParam; if (std::find (usedParametersId_.begin(),usedParametersId_.end(),usedYParam->getId()) == usedParametersId_.end()) usedParametersId_.push_back(usedYParam->getId()); if (originalXSerieParam == originalYParam) { //same original parameter => re-use yparam usedXParam = usedYParam; } else { //create resampled parameter under times of yparam for cparam usedXParam = createSampledParameterUnderReferenceParameter(originalXSerieParam, originalYParam); if (std::find (usedParametersId_.begin(),usedParametersId_.end(),usedXParam->getId()) == usedParametersId_.end()) usedParametersId_.push_back(usedXParam->getId()); } } break; } //link serie to this resampled parameter ity->setParamId(usedYParam->getId()); xSerie.setParamId(usedXParam->getId()); if (originalColorParam != nullptr) { AMDA::Parameters::ParameterSPtr usedColorParam = createSampledParameterUnderReferenceParameter(originalColorParam, usedYParam); //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->setColorParamId(usedColorParam->getId()); //link the used parameter to the color serie colorSerieParameterAxes->getColorSeriePropertiesById(ity->getColorSerieId()).addParamId(usedYParam->getId(), usedColorParam->getId()); //activate the Z Axis ity->setZAxis(true); } } } } /* * Dumps properties for test. */ void XYPlot::dump(std::ostream& out_){ PanelPlotOutput::dump(out_); out_ << "isotropic=" << std::boolalpha << _isIsotropic; } void XYPlot::calculatePlotArea(const Bounds& panelBounds_, Bounds& bounds_) { PanelPlotOutput::calculatePlotArea(panelBounds_, bounds_); if (_isIsotropic) { // Get bounds of panel Bounds lPanelBounds(_panel->getBoundsInPlPage()); // Parse each parameter to define size of axis. double lHorizontalAxisGap = 0; Range lHorizontalRange; double lVerticalAxisGap = 0; Range lVerticalRange; SeriesProperties lSeriesProperties; boost::shared_ptr lYAxis; boost::shared_ptr lXAxis; for (auto param: _parameterAxesList) { // Get Y axis for(auto lSeriesProperties: param.getYSeriePropertiesList()) { lYAxis = _panel->getAxis(lSeriesProperties.getYAxisId()); if (lYAxis.get() == nullptr) { std::stringstream lError; lError << "XYPlot::calculatePlotArea" << ": Y axis with id '" << lSeriesProperties.getYAxisId() << "' not found."; BOOST_THROW_EXCEPTION(PanelPlotOutputException() << AMDA::ex_msg(lError.str())); } lVerticalRange = lYAxis->getRange(); // Get gap for this parameter data if (lVerticalRange.isSet()) { double lTmpVerticalAxisGap = fabs(lVerticalRange.getMin() - lVerticalRange.getMax()); // We get the maximum gap in case where there are several parameter to draw on Y axis(axes). lVerticalAxisGap = std::max(lVerticalAxisGap, lTmpVerticalAxisGap); } lXAxis = _panel->getAxis(lSeriesProperties.getXAxisId()); if (lXAxis.get() == nullptr) { std::stringstream lError; lError << "XYPlot::calculatePlotArea" << ": X axis with id '" << lSeriesProperties.getXAxisId() << "' not found."; BOOST_THROW_EXCEPTION(PanelPlotOutputException() << AMDA::ex_msg(lError.str())); } lHorizontalRange = lXAxis->getRange(); // Get gap for this parameter data if (lHorizontalRange.isSet()) { double lTmpHorizontalAxisGap = fabs(lHorizontalRange.getMin() - lHorizontalRange.getMax()); lHorizontalAxisGap = std::max(lHorizontalAxisGap, lTmpHorizontalAxisGap); } } // If one of gap is 0 do nothing (gap of range can't be 0). if ((lVerticalAxisGap == 0) || (lHorizontalAxisGap == 0)) continue; double lRequestedRatio = lVerticalAxisGap / lHorizontalAxisGap; double lPlotAreaRatio = bounds_._height / bounds_._width; double pageWidth = std::get < 1 > (_panel->_page->getSize()); double pageHeight = std::get < 0 > (_panel->_page->getSize()); double lPageRatio = 0; // Page width and height doesn't depend on orientation... // so we computes page ratio depending on orientation... if (_panel->_page->_orientation == PlotCommon::Orientation::LANDSCAPE) lPageRatio = pageWidth / pageHeight; else lPageRatio = pageHeight / pageWidth; // Calculate width of plot area to keep uniformity of the two scale (axes). double lPlotAreaIsoW = ((bounds_._height * lPageRatio) / lRequestedRatio); // Calculate height of plot area to keep uniformity of the two scale (axes). double lPlotAreaIsoH = ((bounds_._width / lPageRatio) * lRequestedRatio); // When ratio is lower than 1. it signifies that plot area width is bigger than height. // So reduce width. // The upper condition is only valid if new width is purely lower than current plot area width. // Otherwise we must modify height. if ( ((lPlotAreaRatio < 1.) && (lPlotAreaIsoW < bounds_._width)) || ((lPlotAreaRatio > 1.) && (lPlotAreaIsoH > bounds_._height))) { // Calculate X min bounds of plot area if it can be centered. double lPlotAreaIsoX = ((lPanelBounds._width - lPlotAreaIsoW) / 2.) + lPanelBounds._x; // Check if plot area can be centered. if (lPlotAreaIsoX > bounds_._x) { bounds_._x = lPlotAreaIsoX; } else { bounds_._x += (bounds_._width - lPlotAreaIsoW) / 2.; } bounds_._width = lPlotAreaIsoW; } else { // Calculate Y min bounds of plot area if it can be centered. double lPlotAreaIsoY = ((lPanelBounds._height - lPlotAreaIsoH) / 2.) + lPanelBounds._y; // Check if plot area can be centered. if (lPlotAreaIsoY > bounds_._y) { bounds_._y = lPlotAreaIsoY; } else { bounds_._y += (bounds_._height - lPlotAreaIsoH) / 2.; } bounds_._height = lPlotAreaIsoH; } } } } std::string XYPlot::formatDateTime (double dateTime, const std::string &format) { long int lTime = static_cast(dateTime); tm * lTimeTm = gmtime(&lTime); // Format date. char timeBuf[80]; strftime (timeBuf, sizeof (timeBuf), format.c_str(), lTimeTm); return std::string(timeBuf); } void XYPlot::drawTimeTicks(SeriesProperties& pSeries, AMDA::Common::ParameterIndexComponent pParamIndex, double* timeValues, int nbTimeValues) { LOG4CXX_DEBUG(gLogger, "XYPlot::drawTimeTicks"); TimeTickProperties ttProps = pSeries.getTimeTickProperties(); // Draw Time ticks if required ! if ((ttProps.getStep().compare("0") == 0) && (ttProps.getNumber() == 0)) return; // Get parameter sampling, data, start time and end time ParameterAxes* xparameter = getParameterAxesByXSerieId(pSeries.getXId()); XSeriesProperties& xSerie = xparameter->getXSeriePropertiesById(pSeries.getXId()); std::string paramId = pSeries.getParamId(); ParameterData& xData = (*_pParameterValues)[xSerie.getParamId()]; ParameterData& yData = (*_pParameterValues)[paramId]; double startTime = timeValues[0]; double stopTime = timeValues[nbTimeValues-1]; double totalDuration = stopTime - startTime; // LOG4CXX_DEBUG(gLogger, "drawTimeTicks totalDuration " << totalDuration); // Compute x and y coordinates for major and minor time ticks int nbTotMajor = 0; int nbTotMinor = 0; int nbMinorPerMajor = ttProps.getMinor(); if (ttProps.getStep().compare("0") != 0) { double deltaMajorTick, deltaMinorTick; if (ttProps.getStep().compare("auto") == 0) { // LOG4CXX_DEBUG(gLogger, "drawTimeTicks auto"); deltaMajorTick = TimeAxis::computeAutoMajorTickSpace (startTime, stopTime); } else { // LOG4CXX_DEBUG(gLogger, "drawTimeTicks time step"); deltaMajorTick = DD_Time2Double (ttProps.getStep().c_str()); } nbTotMajor = (int) (totalDuration / deltaMajorTick) + 1; deltaMinorTick = deltaMajorTick / (nbMinorPerMajor + 1); // Update totalDuration & stopTime depending on the nbMinorPerMajor value if (nbMinorPerMajor == 0) { nbTotMinor = 0; totalDuration = (nbTotMajor - 1) * deltaMajorTick; stopTime = startTime + totalDuration; } else { nbTotMinor = (nbTotMajor-1) * nbMinorPerMajor; nbTotMinor += (int) ((totalDuration - (nbTotMajor - 1) * deltaMajorTick) / deltaMinorTick); totalDuration = (nbTotMajor + nbTotMinor - 1) * deltaMinorTick; stopTime = startTime + totalDuration; } // LOG4CXX_DEBUG(gLogger, "drawTimeTicks totalDuration " << totalDuration); } else if (ttProps.getNumber() != 0) { // LOG4CXX_DEBUG(gLogger, "drawTimeTicks step number"); nbTotMajor = ttProps.getNumber(); nbTotMinor = (nbTotMajor-1) * nbMinorPerMajor; } // LOG4CXX_DEBUG(gLogger, "drawTimeTicks nbMajor " << nbTotMajor); // LOG4CXX_DEBUG(gLogger, "drawTimeTicks nbMinor " << nbTotMinor); // LOG4CXX_DEBUG(gLogger, "drawTimeTicks deltaMajorDuration " << deltaMajorTick); // Build & compute graduation double *majorX, *majorY, *minorX, *minorY, *tickLabel; majorX = new double [nbTotMajor]; majorY = new double [nbTotMajor]; minorX = new double [nbTotMinor]; minorY = new double [nbTotMinor]; tickLabel = new double [nbTotMajor]; int nbGrad = nbTotMajor + nbTotMinor; int curMajorGrad = 0; int curMinorGrad = 0; double prevTime = 0; double nextTime = 0; for (int grad=0; gradgetAxis(pSeries.getXAxisId())->getRange().getMin(), _panel->getAxis(pSeries.getXAxisId())->getRange().getMax(), _panel->getAxis(pSeries.getYAxisId())->getRange().getMin(), _panel->getAxis(pSeries.getYAxisId())->getRange().getMax()); _pls->wind(std::get<0>(lPlWindow), std::get<1>(lPlWindow), std::get<2>(lPlWindow), std::get<3>(lPlWindow)); if (nbTotMajor != 0) { drawSymbols( ttProps.getFirstSymbol().getType(), ttProps.getFirstSymbol().getSize(), 1., ttProps.getFirstSymbol().getColor(), 1, majorX, majorY); drawSymbols( ttProps.getSymbol().getType(), ttProps.getSymbol().getSize(), 1., ttProps.getSymbol().getColor(), nbTotMajor-1, &majorX[1], &majorY[1]); } if (nbTotMinor != 0) { drawSymbols( ttProps.getSymbol().getType(), ttProps.getSymbol().getSize(), 0.5, ttProps.getSymbol().getColor(), nbTotMinor, minorX, minorY); } // Draw text Label for major tick by building a list of textPlots if (nbTotMajor != 0) { TextPlots textPlots; TextPlot baseTextPlot; baseTextPlot._color = ttProps.getColor(); baseTextPlot.setFont(ttProps.getFont()); std::string computedTimeFormat = getPlTimeFormat (std::string("hh:mm"), startTime, stopTime, nbTotMajor); // LOG4CXX_DEBUG(gLogger, "drawTimeTicks computedTimeFormat" << computedTimeFormat); Range lYRange = getYAxisRange (pSeries, _panel->getAxis(pSeries.getYAxisId())); double deltaX = 0; // (lXRange.getMax () - lXRange.getMin ()) / 80; double deltaY = (lYRange.getMax () - lYRange.getMin ()) / 80; for (int g=0; g textPlot (new TextPlot (baseTextPlot)); textPlot->_x = boost::lexical_cast(majorX [g] + deltaX); textPlot->_y = boost::lexical_cast(majorY [g] + deltaY); textPlot->_text = formatDateTime (tickLabel [g], computedTimeFormat); textPlots.push_back (textPlot); } // Draw textPlots for major graduations drawTextPlots (_panel->getAxis(pSeries.getXAxisId()), _panel->getAxis(pSeries.getYAxisId()), lPlWindow, textPlots); } // Free major & minor graduations positions delete [] majorX; delete [] majorY; delete [] minorX; delete [] minorY; delete [] tickLabel; } void XYPlot::drawSeries(double startDate, double stopDate, int intervalIndex, std::string pParamId, SeriesProperties& pSeries, AMDA::Common::ParameterIndexComponent pParamIndex, ParameterAxes& param, bool moreThanOneSerieForAxis) { // This will configure window, draw axes (if needed) and legend of axes. LOG4CXX_DEBUG(gLogger, "XYPlot::drawSeries"); PanelPlotOutput::drawSeries(startDate, stopDate, intervalIndex, pParamId, pSeries, pParamIndex, param, moreThanOneSerieForAxis); //get x serie values double* lXData = NULL; double* lXTime = NULL; int nbXValues = 0; if (!getXComputedValuesFromSerieAndInterval(startDate, stopDate, pSeries, &lXData, &lXTime, nbXValues)) { LOG4CXX_DEBUG(gLogger, "XYPlot::drawSeries - Cannot get computed values for x serie"); return; } //get y serie values double* lYData = NULL; double* lYTime = NULL; int nbYValues = 0; if (!getComputedValuesFromSerieAndInterval(startDate, stopDate, pSeries, pParamIndex, &lYData, &lYTime, nbYValues)) { LOG4CXX_DEBUG(gLogger, "XYPlot::drawSeries - Cannot get computed values for y serie"); return; } double *coloredComputedValues = NULL; double *coloredTimeValues = NULL; //get colored value if needed if (!pSeries.getColorParamId().empty() && (_panel->getColorAxis() != nullptr)) { int nbColoredValues; if (!getColoredComputedValuesFromSerieAndInterval(startDate, stopDate, pSeries, &coloredComputedValues, &coloredTimeValues, nbColoredValues)) { LOG4CXX_DEBUG(gLogger, "XYPlot::drawSeries - Cannot get computed values for colored parameter"); return; } } PlWindow lPlWindow = PlWindow(_panel->getAxis(pSeries.getXAxisId())->getRange().getMin(), _panel->getAxis(pSeries.getXAxisId())->getRange().getMax(), _panel->getAxis(pSeries.getYAxisId())->getRange().getMin(), _panel->getAxis(pSeries.getYAxisId())->getRange().getMax()); _pls->wind(std::get<0>(lPlWindow), std::get<1>(lPlWindow), std::get<2>(lPlWindow), std::get<3>(lPlWindow)); //draw serie Color lineColor = getSerieLineColor(pSeries, moreThanOneSerieForAxis); Color symbolColor = getSerieSymbolColor(pSeries, lineColor); drawSymbols( pSeries.getSymbolProperties().getType(), pSeries.getSymbolProperties().getSize(), 1., symbolColor, nbYValues, lXData, lYData, coloredComputedValues); drawLines( pSeries.getLineProperties().getType(), pSeries.getLineProperties().getStyle(), pSeries.getLineProperties().getWidth(), lineColor, nbYValues, lXData, lYData, coloredComputedValues); // Draw eventual Time ticks drawTimeTicks(pSeries, pParamIndex, lYTime, nbYValues); //draw interval drawSerieInterval(pSeries,lXData,lYData,lYTime,nbYValues,intervalIndex); //add serie to param legend addSerieToParamsLegend(pSeries,pParamIndex,param._originalParamId, lineColor,symbolColor,startDate, stopDate, intervalIndex); delete[] lXData; delete[] lYData; if (coloredComputedValues != NULL) delete[] coloredComputedValues; } /** * Draws Curve */ void XYPlot::drawCurvePlot(CurvePlot &curvePlot) { if (curvePlot.isDrawn()) return; LOG4CXX_DEBUG(gLogger, "XYPlot::drawCurvePlot"); //retrieve associated y serie properties for (ParameterAxesList::iterator it = _parameterAxesList.begin(); it != _parameterAxesList.end(); ++it) { std::vector::iterator ity; for (ity = it->getYSeriePropertiesList().begin(); ity != it->getYSeriePropertiesList().end(); ++ity) { if (curvePlot._serieId == ity->getId()) { //get curve points int resolution = ity->getMaxResolution(); if (resolution <= 0) resolution = 100; CurveFunctionWriter::CurvePointList pointList = curvePlot.getPointList(resolution); if (pointList.empty()) { return; //nothing to plot } //prepare data in relation to axis of the associated serie double xData[pointList.size()]; double yData[pointList.size()]; double xMin = DBL_MAX; double xMax = -DBL_MAX; double yMin = DBL_MAX; double yMax = -DBL_MAX; int i = 0; for (auto point : pointList) { xData[i] = point.x; xMin = std::min(xMin,point.x); xMax = std::max(xMax,point.x); yData[i] = point.y; yMin = std::min(yMin,point.y); yMax = std::max(yMax,point.y); ++i; } boost::shared_ptr lXAxis(_panel->getAxis(ity->getXAxisId())); if ((lXAxis == nullptr)) { std::stringstream lError; lError << "XYPlot::drawCurvePlot : cannot retrieve X Axis"; BOOST_THROW_EXCEPTION( PanelPlotOutputException() << AMDA::ex_msg(lError.str())); } double* lXData = _panel->getAxis(ity->getXAxisId())->getComputedValues( xData, pointList.size(), xMin, xMax); if(lXData == NULL) { std::stringstream lError; lError << "XYPlot::drawCurvePlot : no value for x, check the request x parameter definition"; BOOST_THROW_EXCEPTION( PanelPlotOutputException() << AMDA::ex_msg(lError.str())); } boost::shared_ptr lYAxis(_panel->getAxis(ity->getYAxisId())); if ((lYAxis == nullptr)) { std::stringstream lError; lError << "XYPlot::drawCurvePlot : cannot retrieve Y Axis"; BOOST_THROW_EXCEPTION( PanelPlotOutputException() << AMDA::ex_msg(lError.str())); } double* lYData = _panel->getAxis(ity->getYAxisId())->getComputedValues( yData, pointList.size(), yMin, yMax); if(lYData == NULL) { std::stringstream lError; lError << "XYPlot::drawCurvePlot : no value for y, check the request y parameter definition"; BOOST_THROW_EXCEPTION( PanelPlotOutputException() << AMDA::ex_msg(lError.str())); } Range lXRange = lXAxis->getRange(); Range lYRange = lYAxis->getRange(); PlWindow lPlWindow = PlWindow(lXRange.getMin(), lXRange.getMax(), lYRange.getMin(), lYRange.getMax()); _pls->wind(std::get<0>(lPlWindow), std::get<1>(lPlWindow), std::get<2>(lPlWindow), std::get<3>(lPlWindow)); //draw the curve drawLines( LineType::LINE, curvePlot._style, curvePlot._width, curvePlot._color, pointList.size(), lXData, lYData); curvePlot.setDrawn(true); //delete data delete [] lXData; delete [] lYData; return; } } } //cannot retrieve the associted y serie => exception std::stringstream lError; lError << "XYPlot::drawCurvePlots : y serie with id " << curvePlot._serieId << " not found."; BOOST_THROW_EXCEPTION(PanelPlotOutputException() << AMDA::ex_msg(lError.str())); } void XYPlot::configureSeriesAxis() { // map std::map lAxisRangeMap; Range lColorAxeRange; boost::shared_ptr lZAxis = _panel->getColorAxis(); SeriesProperties lSeriesProperties; // Parse each parameter to define on which axis to draw series. for (auto param: _parameterAxesList) { // Get number of series to draw // For each "Y" index of parameter identify on which axis series must be drawn. // Also configure X axis for range. for(auto lSeriesProperties: param.getYSeriePropertiesList()) { boost::shared_ptr lYAxis = _panel->getAxis(lSeriesProperties.getYAxisId()); if (lYAxis.get() == nullptr) { std::stringstream lError; lError << "XYPlot::configureSeriesAxis" << ": Y axis with id '" << lSeriesProperties.getYAxisId() << "' not found."; BOOST_THROW_EXCEPTION(PanelPlotOutputException() << AMDA::ex_msg(lError.str())); } lYAxis->_used = true; // Configure range for Y axis. Range lYRange(lYAxis->getRange()); // If range status for this axis is set by the user do not update range "automatically". if (isnan(lYRange.getMin()) && isnan(lYRange.getMax())) { ParameterData& yData = (*_pParameterValues)[lSeriesProperties.getParamId()]; Range lEstimatedRange(lAxisRangeMap[lYAxis->_id]); for (auto index : lSeriesProperties.getIndexList(_pParameterValues)) { Range lParamIndexRange( yData.getMin(index), yData.getMax(index)); if (isnan(lEstimatedRange.getMin()) && isnan(lEstimatedRange.getMax())) { lEstimatedRange.setMin(lParamIndexRange.getMin()); lEstimatedRange.setMax(lParamIndexRange.getMax()); } else { lEstimatedRange.setMin(std::min(lEstimatedRange.getMin(), lParamIndexRange.getMin())); lEstimatedRange.setMax(std::max(lEstimatedRange.getMax(), lParamIndexRange.getMax())); } fixRange(lEstimatedRange, lYAxis->_scale == Axis::Scale::LOGARITHMIC); if(lEstimatedRange.getMin()==lEstimatedRange.getMax()) lEstimatedRange.setMargin(0.05); } lEstimatedRange._extend = lYRange._extend; lAxisRangeMap[lYAxis->_id] = lEstimatedRange; } //Set XAxis range boost::shared_ptr lXAxis = _panel->getAxis(lSeriesProperties.getXAxisId()); if (lXAxis.get() == nullptr) { std::stringstream lError; lError << "XYPlot::configureSeriesAxis" << ": X axis with id '" << lSeriesProperties.getXAxisId() << "' not found."; BOOST_THROW_EXCEPTION(PanelPlotOutputException() << AMDA::ex_msg(lError.str())); } lXAxis->_used = true; Range lXRange(lXAxis->getRange()); // If user didn't set a range, calculate range automatically (based on parameters values). if (!lXRange.isSet()) { Range lEstimatedRange(lAxisRangeMap[lXAxis->_id]); ParameterAxes* xparameter = getParameterAxesByXSerieId(lSeriesProperties.getXId()); XSeriesProperties& xSerie = xparameter->getXSeriePropertiesById(lSeriesProperties.getXId()); ParameterData& xData = (*_pParameterValues)[xSerie.getParamId()]; Range lRangeSampling(xData.getMin(xSerie.getIndex()), xData.getMax(xSerie.getIndex())); if (!lEstimatedRange.isSet()) { lEstimatedRange = lRangeSampling; } else { lEstimatedRange.setMin(std::min(lEstimatedRange.getMin(), lRangeSampling.getMin())); lEstimatedRange.setMax(std::max(lEstimatedRange.getMax(), lRangeSampling.getMax())); } if(lEstimatedRange.getMin()==lEstimatedRange.getMax()) lEstimatedRange.setMargin(0.05); fixRange(lEstimatedRange, lXAxis->_scale == Axis::Scale::LOGARITHMIC); lAxisRangeMap[lXAxis->_id] = lEstimatedRange; } // Set ZAxis range if a color param is defined for this serie if (lZAxis != nullptr) { if (!lSeriesProperties.getColorParamId().empty()) { lZAxis->_used = true; Range lRange(lZAxis->getRange()); ParameterAxes* colorSerieParameterAxes = getParameterAxesByColorSerieId(lSeriesProperties.getColorSerieId()); if (colorSerieParameterAxes == NULL) continue; ColorSeriesProperties& colorSerieProp = colorSerieParameterAxes->getColorSeriePropertiesById(lSeriesProperties.getColorSerieId()); // If range status for this axis is set by the user do not update range "automatically". if (isnan(lRange.getMin()) && isnan(lRange.getMax())) { Range lEstimatedRange(lColorAxeRange); Range lParamIndexRange( (*_pParameterValues)[lSeriesProperties.getColorParamId()].getMin( colorSerieProp.getIndex()), (*_pParameterValues)[lSeriesProperties.getColorParamId()].getMax( colorSerieProp.getIndex())); if (isnan(lEstimatedRange.getMin()) && isnan(lEstimatedRange.getMax())) { lEstimatedRange.setMin(lParamIndexRange.getMin()); lEstimatedRange.setMax(lParamIndexRange.getMax()); } else { lEstimatedRange.setMin(std::min(lEstimatedRange.getMin(), lParamIndexRange.getMin())); lEstimatedRange.setMax(std::max(lEstimatedRange.getMax(), lParamIndexRange.getMax())); } if(lEstimatedRange.getMin()==lEstimatedRange.getMax()) lEstimatedRange.setMargin(0.05); fixRange(lEstimatedRange, lZAxis->_scale == Axis::Scale::LOGARITHMIC); lEstimatedRange._extend = lRange._extend; lColorAxeRange = lEstimatedRange; } } } } } // Update range of axis. for (auto lAxis: lAxisRangeMap) { _panel->getAxis(lAxis.first)->setRange(lAxis.second); } if (lZAxis != nullptr && lColorAxeRange.isSet()) lZAxis->setRange(lColorAxeRange); } void XYPlot::configureAxisLegend () { // Y axis AxisLegendManager::configureYAxisLegendForSeries(this); // X axis AxisLegendManager::configureXAxisLegendForSeries(this); // Z axis AxisLegendManager::configureColorAxisLegendForSeries(this); } /* * @brief Get computed X values (in relation with the x axis definition) for a serie and a time interval * Do not forget to delete computedValues !! * Don't delete timeValues !! */ bool XYPlot::getXComputedValuesFromSerieAndInterval(double startDate, double stopDate, SeriesProperties &rSeriesProperties, double** computedValues, double** timeValues, int& nbValues) { LOG4CXX_DEBUG(gLogger, "XYPlot::getXComputedValuesFromSerieAndInterval"); ParameterAxes* xSerieParameterAxes = getParameterAxesByXSerieId(rSeriesProperties.getXId()); XSeriesProperties& xSerie = xSerieParameterAxes->getXSeriePropertiesById(rSeriesProperties.getXId()); //get parameter x data for this serie ParameterData& xData = (*_pParameterValues)[xSerie.getParamId()]; int startIndex; xData.getIntervalBounds(startDate, stopDate, startIndex, nbValues); if (nbValues == 0) { LOG4CXX_DEBUG(gLogger, "XYPlot::getXComputedValuesFromSerieAndInterval - Cannot find data for the x serie"); return false; } double *valuesInterval = xData.getValues(xSerie.getIndex(),startIndex); //get computed data for interval [startDate, stopDate] in relation with the serie y axis (*computedValues) = _panel->getAxis(rSeriesProperties.getXAxisId())->getComputedValues( valuesInterval, nbValues, xSerie.getMin(), xSerie.getMax()); //get time values (*timeValues) = &xData.getTimes()[startIndex]; return true; } /* * @overload PanelPlotOutput::getSerieParamsLegendString Return the associated params legend to a xy serie */ std::string XYPlot::getSerieParamsLegendString(SeriesProperties &rSeriesProperties, AMDA::Common::ParameterIndexComponent& index, std::string originalParamId) { //retrieve x axis properties ParameterAxes* xparameter = getParameterAxesByXSerieId(rSeriesProperties.getXId()); XSeriesProperties& xSerie = xparameter->getXSeriePropertiesById(rSeriesProperties.getXId()); // Retrieve ParamInfo Manager ParamMgr *piMgr =ParamMgr::getInstance(); // Build parameter text legend depending on the availability of paramInfo // for Y and X component //For y component, re-use the text of the PanelPlotOutput std::stringstream paramLegendText; paramLegendText << PanelPlotOutput::getSerieParamsLegendString(rSeriesProperties,index,originalParamId); // Try to retrieve informations from paramInfo for Y componenet //ParameterSPtr p = _parameterManager.getParameter(rSeriesProperties.getParamId()); paramLegendText << " = f("; // Try to retrieve informations from paramInfo for X component ParameterSPtr p = _parameterManager.getParameter(xparameter->_originalParamId); ParamInfoSPtr paramInfo = piMgr->getParamInfoFromId(p->getInfoId()); // Build parameter text legend depending on the availability of paramInfo if (paramInfo) { if ((xSerie.getIndex().getDim1Index() == -1) && (xSerie.getIndex().getDim2Index() == -1)) { // parameter legend text = short_name paramLegendText << paramInfo->getShortName(); } else { if (paramInfo->getComponents(xSerie.getIndex()).empty() == false) // parameter legend text = components at index index] paramLegendText << paramInfo->getComponents(xSerie.getIndex()); else { // parameter legend text = short_name [index] paramLegendText << paramInfo->getShortName() << "[" << xSerie.getIndex().getDim1Index(); if (xSerie.getIndex().getDim2Index() != -1) paramLegendText << "," << xSerie.getIndex().getDim2Index(); paramLegendText << "]"; } } } else { if ((xSerie.getIndex().getDim1Index() == -1) && (xSerie.getIndex().getDim2Index() == -1)) // parameter legend text = _originalParamId paramLegendText << xparameter->_originalParamId; else { // parameter legend text = _originalParamId [index] paramLegendText << xparameter->_originalParamId << "[" << xSerie.getIndex().getDim1Index(); if (xSerie.getIndex().getDim2Index() != -1) paramLegendText << "," << xSerie.getIndex().getDim2Index(); paramLegendText << "]"; } } paramLegendText << ")"; return paramLegendText.str(); } } /* namespace plot */