Logo Search packages:      
Sourcecode: openturns version File versions  Download package

GraphImplementation.cxx

Go to the documentation of this file.
//                                               -*- C++ -*-
/**
 *  @file  GraphImplementation.cxx
 * @brief Graph implements graphic devices for plotting through R
 *
 *  (C) Copyright 2005-2007 EDF-EADS-Phimeca
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License.
 *
 *  This library is distributed in the hope that it will be useful
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
 *
 *  @author: $LastChangedBy: dutka $
 *  @date:   $LastChangedDate: 2008-09-13 22:37:56 +0200 (sam 13 sep 2008) $
 *  Id:      $Id: GraphImplementation.cxx 929 2008-09-13 20:37:56Z dutka $
 */
#include <cstdlib>
#include <fstream>
#include "GraphImplementation.hxx"
#include "Rfunctions.hxx"
#include "Path.hxx"
#include "PersistentObjectFactory.hxx"
#include "ResourceMap.hxx"
#include "Log.hxx"

namespace OpenTURNS
{

  namespace Base
  {

    namespace Type
    {

      using Graph::Drawable;

      TEMPLATE_CLASSNAMEINIT(PersistentCollection<Drawable>);

      static Common::Factory<PersistentCollection<Drawable> > RegisteredFactory1("PersistentCollection<Drawable>");
    } /* namespace Type */

    namespace Graph
    {
      CLASSNAMEINIT(GraphImplementation);

      static Common::Factory<GraphImplementation> RegisteredFactory("GraphImplementation");

      typedef Common::Path                  Path;
      typedef Common::Log                   Log;
      typedef Stat::NumericalSample         NumericalSample;
      typedef Common::InvalidRangeException InvalidRangeException;

      const String GraphImplementation::NoSpecifiedLabel = "";

      const UnsignedLong GraphImplementation::BoundingBoxSize;

00066       const UnsignedLong GraphImplementation::DefaultWidth;
      const UnsignedLong GraphImplementation::DefaultHeight;
      const NumericalScalar GraphImplementation::DefaultLegendFontSize;
      Bool GraphImplementation::IsFirstInitialization = true;


      GraphImplementation::Description GraphImplementation::ValidLegendPositions;

      /* Initialize valid legend positions */
      void GraphImplementation::initializeValidLegendPositions()
      {
      ValidLegendPositions.setName("ValidLegendPositions");
      ValidLegendPositions.add(NoSpecifiedLabel);
      ValidLegendPositions.add("bottomright");
      ValidLegendPositions.add("bottom");
00081       ValidLegendPositions.add("bottomleft");
      ValidLegendPositions.add("left");
      ValidLegendPositions.add("topleft");
      ValidLegendPositions.add("topright");
      ValidLegendPositions.add("right");
      ValidLegendPositions.add("center");
      }

      /* Default constructor */
      GraphImplementation::GraphImplementation():
      PersistentObject(NoSpecifiedLabel),
      title_(NoSpecifiedLabel),
      legendPosition_(NoSpecifiedLabel),
      legendFontSize_(DefaultLegendFontSize),
      xTitle_(NoSpecifiedLabel),
      yTitle_(NoSpecifiedLabel),
      showAxes_(true),
      showGrid_(true),
      automaticBoundingBox_(true),
      boundingBox_(BoundingBoxSize),
      drawablesCollection_(0),
      path_(NoSpecifiedLabel),
00103       fileName_(NoSpecifiedLabel)
      {
      if(IsFirstInitialization){
        initializeValidLegendPositions();
        IsFirstInitialization = false;
      }
      }

      /* Constructor with parameters */
      GraphImplementation::GraphImplementation(const String & title,
                                     const String & xTitle,
                                     const String & yTitle,
                                     const Bool showAxes,
                                     const String & legendPosition,
                                               const NumericalScalar legendFontSize) throw(InvalidArgumentException):
      PersistentObject(title),
      title_(title),
      legendFontSize_(legendFontSize),
      xTitle_(xTitle),
      yTitle_(yTitle),
      showAxes_(showAxes),
      showGrid_(true),
      automaticBoundingBox_(true),
      boundingBox_(BoundingBoxSize),
      drawablesCollection_(0),
      path_(NoSpecifiedLabel),
      fileName_(NoSpecifiedLabel)
      {
      if(IsFirstInitialization)
        {
          initializeValidLegendPositions();
          IsFirstInitialization = false;
00135         }
      // Check if the given legend position is valid

      if(!isValidLegendPosition(legendPosition)) throw InvalidArgumentException(HERE) << "The given legend position = " << legendPosition << " is invalid";

      legendPosition_ = legendPosition;
00141       }
      
      /* Virtual constructor */
      GraphImplementation * GraphImplementation::clone() const
      {
      return new GraphImplementation(*this);
      }

      /* String converter */
      String GraphImplementation::str() const
      {
      OSS oss;
      oss << "class=" << GraphImplementation::GetClassName()
          << " name=" << getName()
          << " title=" << title_
          << " xTitle=" << xTitle_
          << " yTitle=" << yTitle_
          << " axes=" << (showAxes_?"ON":"OFF")
00159           << " grid=" << (showGrid_?"ON":"OFF")
          << " legendposition="<< legendPosition_
          << " legendFontSize=" << legendFontSize_
          << " drawables=" << drawablesCollection_
          << " fileName=" << getFileName();
      return oss;
00165       }

      /* Adds a drawable instance to the collection of drawables contained in GraphImplementation */
      void GraphImplementation::addDrawable(const Drawable & aDrawable)
      {
      drawablesCollection_.add(aDrawable);
      }

      /** Drawables accessor */
      GraphImplementation::DrawableCollection GraphImplementation::getDrawables() const
      {
00176       return drawablesCollection_;
      }

      void GraphImplementation::setDrawables(const DrawableCollection & drawableCollection)
      {
      drawablesCollection_ = drawableCollection;
      }

      /** Individual drawable accessor */
      Drawable GraphImplementation::getDrawable(const UnsignedLong index) const
      {
      if (index >= drawablesCollection_.getSize()) throw InvalidRangeException(HERE) << "Error: trying to get a drawable at position " << index << " from a collection of size " << drawablesCollection_.getSize();
      return drawablesCollection_[index];
00189       }

      void GraphImplementation::setDrawable(const Drawable & drawable, const UnsignedLong index)
      {
      if (index >= drawablesCollection_.getSize()) throw InvalidRangeException(HERE) << "Error: trying to set a drawable at position " << index << " into a collection of size " << drawablesCollection_.getSize();
      drawablesCollection_[index] = drawable;
00195       }

      /* Hide or show x and y axes */
      void GraphImplementation::setAxes(const Bool showAxes)
      {
      showAxes_ = showAxes;
00201       }
      
      /* Accessor for showAxes_ */
      Bool GraphImplementation::getAxes() const
      {
      return showAxes_;
00207       }
      
      /* Hide or show x grid */
      void GraphImplementation::setGrid(const Bool showGrid)
      {
      showGrid_ = showGrid;
00213       }
      
      /* Accessor for showGrid_ */
      Bool GraphImplementation::getGrid() const
      {
      return showGrid_;
00219       }
      
      /* Accesor for xTitle */
      String GraphImplementation::getXTitle() const
      {
      return xTitle_;
00225       }

      /* Accessor for xTitle */
      void GraphImplementation::setXTitle(const String & title)
      {
      xTitle_ = title;
00231       }
      
      /* Accessor for yTitle */
      String GraphImplementation::getYTitle() const
      {
      return yTitle_;
00237       }
      
      /* Accessor for yTitle */
      void GraphImplementation::setYTitle(const String & title)
      {
      yTitle_ = title;
00243       }

      /* Accesor for title */
      String GraphImplementation::getTitle() const
      {
      return title_;
00249       }
      
      /* Accesor for title */
      void GraphImplementation::setTitle(const String & title)
      {
      title_ = title;
00255       }

      /* Accessor for path */
      String GraphImplementation::getPath() const
      {
      return path_;
00261       }
      
      /* Accessor for file name */
      String GraphImplementation::getFileName() const
      {
      return fileName_;
      }
      
      /* Build the R command corresponding to the legend */
      String GraphImplementation::makeRLegendCommand() const
      {
      OSS labels, colors, lines, points, fill;
      labels << "c(";
      colors << "c(";
      lines << "c(";
      points << "c(";
      fill << "c(";
      
      Bool drawLegend = false;

      for(DrawableCollection::const_iterator it = drawablesCollection_.begin(); it != drawablesCollection_.end(); ++it)
        {
          if(it->getLegendName() != NoSpecifiedLabel)
            {
            drawLegend = true;
            labels << "\"" << it->getLegendName() << "\",";

            if(it->getColor() == NoSpecifiedLabel)
              colors << "NA,";
            else colors << "\"" << it->getColor() << "\",";

            if(it->getFillStyle() == NoSpecifiedLabel)
              fill << "NA,";
            else fill << "\"" << it->getFillStyle() << "\",";

            if(it->getPointStyle() == NoSpecifiedLabel || it->getFillStyle() != NoSpecifiedLabel) //cannot merge fill and point symbol
              points << "NA,";
            else points << it->getPointCode(it->getPointStyle())<< ",";

            if(it->getLineStyle() == NoSpecifiedLabel || it->getFillStyle() != NoSpecifiedLabel ) //cannot merge line and fill symbol
              lines << "NA,";
            else lines << "\"" << it->getLineStyle() << "\",";
            }
        }
      if(drawLegend){
        String labels_str(labels);
        labels_str.replace(labels_str.length() - 1, 1, ")");
        
        String colors_str(colors);
        colors_str.replace(colors_str.length() - 1, 1, ")");
        
        String lines_str(lines);
        lines_str.replace(lines_str.length() - 1, 1, ")");
        
        String points_str(points);
        points_str.replace(points_str.length() - 1, 1, ")");
      
        String fill_str(fill);
        fill_str.replace(fill_str.length() - 1, 1, ")");

        OSS rCommand;
        rCommand << "legend(\"" << legendPosition_ << "\","
               << "legend=" << labels_str << ","
               << "col=" << colors_str << ","
               << "lty=" << lines_str << ","
               << "pch=" << points_str << ","
               << "fill="<< fill_str << ","
               << "cex=" << legendFontSize_ << ","
00329                << "bg=\"grey90\",merge=TRUE,density=40)";

        return rCommand;
      }
      
      else return NoSpecifiedLabel;
00335       }

      /* Get the R command corresponding to the graph */
      String GraphImplementation::getRCommand() const
      {
      return OSS() << makeRHeaderCommand() << makeRCoreCommand();
00341       }

      /* Make R header commande */
      String GraphImplementation::makeRHeaderCommand() const
      {
      return OSS() << R_LEGEND << "\n" << "\n" << R_PIE << "\n" << "options(digits=17)" << "\n" << "options(warn=-1)" << "\n";
      }

      /* Make R core command */
      String GraphImplementation::makeRCoreCommand() const
      {
      // get the general bounding box
      BoundingBox boundingBox(getBoundingBox());

      //load the R code attached to the general plot
      OSS graphCommand;
      graphCommand << "plot(c(" << boundingBox[0] << "," << boundingBox[1] << "),"
                 << "c(" << boundingBox[2] << "," << boundingBox[3] << "),"
                 << "type=\"n\",main=\"" << title_ << "\",";
      if (showAxes_)
        {
          graphCommand << "xlab=\"" << xTitle_ << "\",ylab=\"" << yTitle_ << "\","
                       << "axes=TRUE";
        }
      else
        {
          graphCommand << "xlab=\"\",ylab=\"\",axes=FALSE";
          }
      if (showGrid_)
        {
          graphCommand << ", panel.first=grid()";
        }
      graphCommand << ", cex.main=2, cex.axis=1.5, cex.lab=1.5)\n";

      // add the R code attached to each drawable
      UnsignedLong drawablesSize(drawablesCollection_.getSize());
      for(UnsignedLong i = 0; i < drawablesSize; ++i)
        {
00379           if (drawablesCollection_[i].getData().getSize() != 0)
            graphCommand << drawablesCollection_[i].draw() << "\n";
        }
      // make the legend command
      graphCommand << (legendPosition_ == NoSpecifiedLabel ? NoSpecifiedLabel : makeRLegendCommand());
      return graphCommand;
      }

      /* The method that generates the graphic files */
      void GraphImplementation::draw(const String & path,
                             const String & file,
                             const NumericalScalar width,
                             const NumericalScalar height,
                             const Format format) throw(InternalException)
      {
      path_ = path;
      fileName_ = file;
      Format drawingFormat(format);
      // Check the needed drawingFormat. If it is invalid, ste it to ALL
      if ((drawingFormat != ALL) && (drawingFormat != PNG) && (drawingFormat != EPS) && (drawingFormat != FIG))
        {
          drawingFormat = ALL;
        }
      OSS rCommand;
      rCommand << makeRHeaderCommand();
      String rCoreCommand(makeRCoreCommand());
      if ((drawingFormat == ALL) || (drawingFormat == EPS))
        {
          rCommand << "postscript(\"" << getPostscript() << "\", horizontal = FALSE, onefile = FALSE, paper = \"special\", height=" << height / 72. << ", width=" << width / 72. << ")" << "\n" << rCoreCommand << "\n" << "dev.off()" << "\n";
        }
      if ((drawingFormat == ALL) || (drawingFormat == PNG))
        {
          rCommand << "bitmap(\"" << getBitmap() << "\",type=\"png16m\",height=" << height / 72. << ", width=" << width / 72. << ", res=72.)" << "\n" << rCoreCommand << "\n" << "dev.off()" << "\n";
        }
      if ((drawingFormat == ALL) || (drawingFormat == FIG))
        {
          rCommand << "xfig(\"" << getVectorial() << "\", horizontal = FALSE, onefile = FALSE, paper = \"A4\", height=" << height / 72. << ", width=" << width / 72. << ")" << "\n" << rCoreCommand << "\n" << "dev.off()" << "\n";
        }

      //vsystem commands to write R code in a temporary file
      /* using mkstemp non standard C for temporary file generation */
      String temporaryFileName(Path::BuildTemporaryFileName("tmp_graph.R.XXXXXX"));
      std::ofstream cmdFile(temporaryFileName.c_str(), std::ios::out);
      cmdFile << String(rCommand);
      cmdFile.close();
      
      //execute R and load R script in temporary file
      OSS systemCommand;
00427       systemCommand << Common::ResourceMap::GetInstance().get("R-executable-command") << " --no-save --silent <" << temporaryFileName << " 2>&1 > /dev/null";
      if (system(String(systemCommand).c_str())) throw InternalException(HERE) << "GraphImplementation: error trying to execute R command=" << String(systemCommand);
#if (OT_DEBUG_LEVEL < 6)
            if(remove(temporaryFileName.c_str()) == -1) throw InternalException(HERE) << "GraphImplementation: error trying to remove file " << temporaryFileName;
#endif
      clean();
      }

      /* Clean temporary files */
      void GraphImplementation::clean()
      {
      UnsignedLong drawableNumber(drawablesCollection_.getSize());
00439       // Clean all the temporary data created by the drawables during their drawing
      for (UnsignedLong i = 0; i < drawableNumber; ++i)
        {
          if (drawablesCollection_[i].getData().getSize() != 0)
            drawablesCollection_[i].clean();
        }
      }

      /* The method that generates the graphic files */
00448       void GraphImplementation::draw(const String & file,
                             const NumericalScalar width,
                             const NumericalScalar height,
                             const Format format) throw(InternalException)
      {
      draw(".", file, width, height, format);
      }

      /* The method returning absolute path of the bitmap graphic file */
      String GraphImplementation::getBitmap() const
00458       {
      String bitmap(path_);
      if( bitmap.find_last_of('/') != (bitmap.length() - 1) )
        bitmap += "/";
      bitmap += fileName_ + ".png";
      return bitmap;
      }

      /* The method returning absolute path of the postscript graphic file */
      String GraphImplementation::getPostscript() const
00468       {
      String postScript(path_);
      if( postScript.find_last_of('/') != (postScript.length() - 1) )
        postScript += "/";
      postScript += fileName_ + ".eps";
      return postScript;
      }

      /* The method returning absolute path of the vectorial graphic file */
      String GraphImplementation::getVectorial() const
00478       {
      String vectorial(path_);
      if( vectorial.find_last_of('/') != (vectorial.length() - 1) )
        vectorial += "/";
      vectorial += fileName_ + ".fig";
      return vectorial;
      }
00485 
      /* Get the bounding box of the whole plot */
      GraphImplementation::BoundingBox GraphImplementation::getBoundingBox() const
      {
      if (automaticBoundingBox_) computeBoundingBox();
      return boundingBox_;
      }

00493       /* Set the bounding box of the whole plot */
      void GraphImplementation::setBoundingBox(const BoundingBox & boundingBox)
      {
      if (boundingBox.getDimension() != BoundingBoxSize) throw InvalidArgumentException(HERE) << "Error: the given bounding box must have a dimension equals to " << BoundingBoxSize << ", here boundingBox=" << boundingBox;
      boundingBox_ = boundingBox;
      automaticBoundingBox_ = false;
      }

      /* Automatic bounding box accessor */
      Bool GraphImplementation::getAutomaticBoundingBox() const
      {
00504         return automaticBoundingBox_;
      }

      void GraphImplementation::setAutomaticBoundingBox(const Bool automaticBoundingBox)
      {
        automaticBoundingBox_ = automaticBoundingBox;
      }

      /* Compute the best bounding box to enclose all the drawables */
      void GraphImplementation::computeBoundingBox() const
      {
      UnsignedLong size(drawablesCollection_.getSize());
      boundingBox_ = BoundingBox(BoundingBoxSize);
      // First exceptional case: no drawable, we default to default bounding box
      if (size == 0)
        {
          Log::Info("Warning: cannot compute the bounding box of a graph with no drawable, switch to [0,1]x[0,1] default bounding box");
          boundingBox_[0] = 0.0;
          boundingBox_[1] = 1.0;
          boundingBox_[2] = 0.0;
          boundingBox_[3] = 1.0;
          return;
        }
      NumericalSample boxes(size, BoundingBoxSize);

      // first, get each Drawable's bounding box and drawing command
      for(UnsignedLong i = 0; i < size; ++i)
        {
          if (drawablesCollection_[i].getData().getSize() != 0)
            boxes[i] = drawablesCollection_[i].getBoundingBox();
        }

      BoundingBox min(boxes.getMin());
      BoundingBox max(boxes.getMax());
      boundingBox_[0]= min[0];
      boundingBox_[1]= max[1];
      boundingBox_[2]= min[2];
      boundingBox_[3]= max[3];
      // All the bounding boxes are degenerated to a point, we default to a 1x1 box centered at this point
      if ((boundingBox_[0] == boundingBox_[1]) || (boundingBox_[2] == boundingBox_[3]))
        {
          Log::Info("Warning: the overall bounding box is degenerated to a point. Switch to a 1x1 box centered at this point");
          boundingBox_[0] -= 0.5;
          boundingBox_[1] += 0.5;
00548           boundingBox_[2] -= 0.5;
          boundingBox_[3] += 0.5;
          return;
        }

      return;
00554       }
      
      /* Get the legend position */
      String GraphImplementation::getLegendPosition() const
      {
      return legendPosition_;
      }

00562       /* Set the legend position */
      void GraphImplementation::setLegendPosition(const String & position) throw(InvalidArgumentException)
      {
      if(!isValidLegendPosition(position)) throw InvalidArgumentException(HERE) << "The given legend position = " << position << " is invalid";
      
      legendPosition_ = position;
      }

      /* Gives all the valid legend positions */
      GraphImplementation::Description GraphImplementation::GetValidLegendPositions()
      {
00573       if(IsFirstInitialization)
        {
          initializeValidLegendPositions();
          IsFirstInitialization = false;
        }
      return ValidLegendPositions;
00579       }

      /* Get the legend font size */
      NumericalScalar GraphImplementation::getLegendFontSize() const
      {
      return legendFontSize_;
      }

00587       /* Set the legend font size */
      void GraphImplementation::setLegendFontSize(const NumericalScalar legendFontSize) throw(InvalidArgumentException)
      {
      if(legendFontSize <= 0.0) throw InvalidArgumentException(HERE) << "The given legend font size = " << legendFontSize << " is invalid";
      
      legendFontSize_ = legendFontSize;
      }

00595       /* check for legend position validity */
      Bool GraphImplementation::isValidLegendPosition(const String & position) const
      {
      const Description::const_iterator it = find(ValidLegendPositions.begin(), ValidLegendPositions.end(), position);
 
      return (it != ValidLegendPositions.end());
      }

      /* Method save() stores the object through the StorageManager */
      void GraphImplementation::save(const StorageManager::Advocate & adv) const
      {
      PersistentObject::save(adv);
      adv.writeValue("title_", title_);
      adv.writeValue("legendPosition_", legendPosition_);
      adv.writeValue("legendFontSize_", legendFontSize_);
00610       adv.writeValue("xTitle_", xTitle_);
      adv.writeValue("yTitle_", yTitle_);
      adv.writeValue("showAxes_", showAxes_);
      adv.writeValue(drawablesCollection_, StorageManager::MemberNameAttribute, "drawablesCollection_");
      adv.writeValue("path_", path_);
      adv.writeValue("fileName_", fileName_);
      }

      /* Method load() reloads the object from the StorageManager */
      void GraphImplementation::load(const StorageManager::Advocate & adv)
      {
      PersistentObject::load(adv);
      String name;
      String stringValue;
      StorageManager::List objList = adv.getList(StorageManager::StringEntity);
      for(objList.firstValueToRead(); objList.moreValuesToRead(); objList.nextValueToRead()) {
        if (objList.readValue(name, stringValue)) {
          if (name == "title_") title_ = stringValue;
          if (name == "legendPosition_") legendPosition_ = stringValue;
          if (name == "xTitle_") xTitle_ = stringValue;
          if (name == "yTitle_") yTitle_ = stringValue;
          if (name == "path_") path_ = stringValue;
          if (name == "fileName_") fileName_ = stringValue;
        }
      }
      NumericalScalar numericalScalarValue;
      objList = adv.getList(StorageManager::NumericalScalarEntity);
      for(objList.firstValueToRead(); objList.moreValuesToRead(); objList.nextValueToRead()) {
        if (objList.readValue(name, numericalScalarValue)) {
          if (name == "legendFontSize_") legendFontSize_ = numericalScalarValue;
        }
      }
      Bool boolValue;
      objList = adv.getList(StorageManager::BoolEntity);
      for(objList.firstValueToRead(); objList.moreValuesToRead(); objList.nextValueToRead()) {
        if (objList.readValue(name, boolValue)) {
          if (name == "showAxes_") showAxes_ = boolValue;
        }
      }
      adv.readValue(drawablesCollection_, StorageManager::MemberNameAttribute, "drawablesCollection_");
      }

    } /* namespace Graph */

  }/* namespace Base */

}/* namespace OpenTURNS */

Generated by  Doxygen 1.6.0   Back to index