1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232
|
/***********************************************/
/**
* @file plotGraph.cpp
*
* @brief Plot functions (x,y).
*
* @author Enrico Kurtenbach
* @author Torsten Mayer-Guerr
* @author Andreas Kvas
* @date 2015-10-20
*
*/
/***********************************************/
// Latex documentation
#define DOCSTRING docstring
static const char *docstring = R"(
Generates a two dimensional xy plot using the GMT Generic Mapping Tools (\url{https://www.generic-mapping-tools.org}).
A variety of image file formats are supported (e.g. png, jpg, eps) determined by the extension of \config{outputfile}.
The plotting area is defined by the two axes \configClass{axisX/Y}{plotAxisType}. An alternative \configClass{axisY2}{plotAxisType}
on the right hand side can be added. The content of the graph itself is defined
by one or more \configClass{layer}{plotGraphLayerType}s.
The plot programs create a temporary directory in the path of \config{outputfile}, writes all needed data into it,
generates a batch/shell script with the GMT commands, execute it, and remove the temporary directory.
With setting \config{options:removeFiles}=false the last step is skipped and it is possible to adjust the plot manually
to specific publication needs. Individual GMT settings are adjusted with \config{options:options}="\verb|FORMAT=value|",
see \url{https://docs.generic-mapping-tools.org/latest/gmt.conf.html}.
See also: \program{PlotDegreeAmplitudes}, \program{PlotMap}, \program{PlotMatrix}, \program{PlotSphericalHarmonicsTriangle}.
)";
/***********************************************/
#include "programs/program.h"
#include "inputOutput/file.h"
#include "plot/plotMisc.h"
#include "plot/plotAxis.h"
#include "plot/plotGraphLayer.h"
#include "plot/plotLegend.h"
#include "plot/plotColorbar.h"
/***********************************************/
/** @brief Plot functions (x,y).
* @ingroup programsGroup */
class PlotGraph
{
public:
void run(Config &config, Parallel::CommunicatorPtr comm);
};
GROOPS_REGISTER_PROGRAM(PlotGraph, SINGLEPROCESS, "Plot functions (x,y)", Plot, TimeSeries)
/***********************************************/
void PlotGraph::run(Config &config, Parallel::CommunicatorPtr /*comm*/)
{
try
{
FileName fileNamePlot;
std::string title;
std::vector<PlotGraphLayerPtr> layer;
PlotAxisPtr axisX, axisY, axisY2;
PlotColorbarPtr colorbar;
PlotLegendPtr legend;
PlotBasics plotBasics;
readConfig(config, "outputfile", fileNamePlot, Config::MUSTSET, "", "*.png, *.jpg, *.eps, ...");
readConfig(config, "title", title, Config::OPTIONAL, "", "");
readConfig(config, "layer", layer, Config::MUSTSET, "", "");
readConfig(config, "axisX", axisX, Config::MUSTSET, "", "");
readConfig(config, "axisY", axisY, Config::MUSTSET, "", "");
readConfig(config, "axisY2", axisY2, Config::OPTIONAL, "", "Second y-axis on right hand side");
readConfig(config, "colorbar", colorbar, Config::OPTIONAL, "", "");
readConfig(config, "legend", legend, Config::OPTIONAL, "1", "");
plotBasics.read(config, "PlotGraph", fileNamePlot, title, "12", "7", {"TIME_SYSTEM=MJD"s});
if(isCreateSchema(config)) return;
// ===========================================================
// colorbar needed?
// ----------------
for(UInt i=0; i<layer.size(); i++)
if(layer.at(i)->requiresColorBar() && !colorbar)
throw(Exception("layer need definition of a colorbar"));
// calculate min and max
// ---------------------
// x-axis
Double minX = 1e99;
Double maxX = -1e99;
for(UInt k=0; k<layer.size(); k++)
layer.at(k)->getIntervalX(axisX->isLogarithmic(), minX, maxX);
axisX->setAutoInterval(minX, maxX);
logInfo<<" plot x range ("<<axisX->getMin()<<" .. "<<axisX->getMax()<<") of ("<<minX<<" .. "<<maxX<<")"<<Log::endl;
// y-axis
Double minY = 1e99;
Double maxY = -1e99;
Double minY2 = 1e99;
Double maxY2 = -1e99;
for(UInt k=0; k<layer.size(); k++)
{
if(axisY2 && layer.at(k)->drawOnSecondAxis())
layer.at(k)->getIntervalY(axisY2->isLogarithmic(), axisX->getMin(), axisX->getMax(), minY2, maxY2);
else
layer.at(k)->getIntervalY(axisY->isLogarithmic(), axisX->getMin(), axisX->getMax(), minY, maxY);
}
axisY->setAutoInterval(minY, maxY);
if(axisY2)
axisY2->setAutoInterval(minY2, maxY2);
logInfo<<" plot y range ("<<axisY->getMin()<<" .. "<<axisY->getMax()<<") of ("<<minY<<" .. "<<maxY<<")"<<Log::endl;
if(axisY2)
logInfo<<" plot y2 range ("<<axisY2->getMin()<<" .. "<<axisY2->getMax()<<") of ("<<minY2<<" .. "<<maxY2<<")"<<Log::endl;
// z-axis
if(colorbar)
{
Double minZ = 1e99;
Double maxZ = -1e99;
for(UInt k = 0; k<layer.size(); k++)
if(axisY2 && layer.at(k)->drawOnSecondAxis())
layer.at(k)->getIntervalZ(colorbar->isLogarithmic(), minX, maxX, minY2, maxY2, minZ, maxZ);
else
layer.at(k)->getIntervalZ(colorbar->isLogarithmic(), minX, maxX, minY, maxY, minZ, maxZ);
colorbar->setAutoInterval(minZ, maxZ);
logInfo<<" plot z range ("<<colorbar->getMin()<<" .. "<<colorbar->getMax()<<") of ("<<minZ<<" .. "<<maxZ<<")"<<Log::endl;
}
// create data files
// -----------------
logStatus<<"create temporary data files"<<Log::endl;
for(UInt i=0; i<layer.size(); i++)
if(axisY2 && layer.at(i)->drawOnSecondAxis())
layer.at(i)->writeDataFile(plotBasics.workingDirectory, i, minX, maxX, minY2, maxY2);
else
layer.at(i)->writeDataFile(plotBasics.workingDirectory, i, minX, maxX, minY, maxY);
axisX->writeDataFile(plotBasics.workingDirectory, "x");
axisY->writeDataFile(plotBasics.workingDirectory, "y");
if(axisY2) axisY2->writeDataFile(plotBasics.workingDirectory, "y2");
// intervals
// ---------
std::string JX, JX2;
{
std::stringstream ss;
ss<<"-R"<<PlotBasics::scriptVariable("range")<<" -JX"<<axisX->direction()*plotBasics.width<<"c"<<axisX->axisModifier()<<"/"<<axisY->direction()*plotBasics.height<<"c"<<axisY->axisModifier();
JX = ss.str();
}
if(axisY2)
{
std::stringstream ss;
ss<<"-R"<<PlotBasics::scriptVariable("rangeY2")<<" -JX"<<axisX->direction()*plotBasics.width<<"c"<<axisX->axisModifier()<<"/"<<axisY2->direction()*plotBasics.height<<"c"<<axisY2->axisModifier();
JX2 = ss.str();
}
// create scriptfile
// -----------------
logStatus<<"create scriptfile"<<Log::endl;
{
OutFile file(plotBasics.fileNameScript());
file<<plotBasics.scriptHeader();
if(colorbar)
file<<colorbar->scriptColorTable();
file<<PlotBasics::scriptSetVariable("range", axisX->getMin()%"%.12e/"s+axisX->getMax()%"%.12e/"s+axisY->getMin()%"%.12e/"s+axisY->getMax()%"%.12e"s)<<std::endl;
if(axisY2)
file<<PlotBasics::scriptSetVariable("rangeY2", axisX->getMin()%"%.12e/"s+axisX->getMax()%"%.12e/"s+axisY2->getMin()%"%.12e/"s+axisY2->getMax()%"%.12e"s)<<std::endl;
// x-axis
file<<"gmt psbasemap -Y"<<-fabs(plotBasics.height)-plotBasics.marginTitle<<"c "<<JX<<" -BSn "<<axisX->scriptEntry("x")<<" -O -K >> groopsPlot.ps"<<std::endl;
// y-axis
file<<"gmt psbasemap "<<JX<<" -BW"<<((axisY2) ? " " : "e ")<<axisY->scriptEntry("y")<<" -O -K >> groopsPlot.ps"<<std::endl;
Bool isAxisY2 = FALSE;
// second y-axis
if(axisY2)
{
file<<"gmt psbasemap "<<JX2<<" -BE "<<axisY2->scriptEntry("y")<<" -O -K >> groopsPlot.ps"<<std::endl;
isAxisY2 = TRUE;
}
// data sets
for(UInt i=0; i<layer.size(); i++)
{
if(isAxisY2 && !layer.at(i)->drawOnSecondAxis())
{
file<<"gmt psxy "<<JX<<" -Bw -T -O -K >> groopsPlot.ps"<<std::endl;
isAxisY2 = FALSE;
}
else if((!isAxisY2) && axisY2 && layer.at(i)->drawOnSecondAxis())
{
file<<"gmt psxy "<<JX2<<" -Be -T -O -K >> groopsPlot.ps"<<std::endl;
isAxisY2 = TRUE;
}
file<<layer.at(i)->scriptEntry();
}
file<<std::endl;
// write axis again
file<<"gmt psbasemap "<<JX<<" -BSn "<<axisX->scriptEntry("x", plotBasics.drawGridOnTop)<<" -O -K >> groopsPlot.ps"<<std::endl;
file<<"gmt psbasemap "<<JX<<" -BW"<<((axisY2) ? " " : "e ")<<axisY->scriptEntry("y", plotBasics.drawGridOnTop)<<" -O -K >> groopsPlot.ps"<<std::endl;
if(axisY2)
file<<"gmt psbasemap "<<JX2<<" -BE "<<axisY2->scriptEntry("y", plotBasics.drawGridOnTop)<<" -O -K >> groopsPlot.ps"<<std::endl;
if(colorbar)
file<<colorbar->scriptEntry(plotBasics.width, plotBasics.height, ((axisY2) ? axisY2->getMargin() : 0.0), axisX->getMargin());
if(legend)
{
std::vector<std::string> legendText;
for(UInt i=0; i<layer.size(); i++)
legendText.push_back(layer.at(i)->legendEntry());
legend->writeDataFile(plotBasics.workingDirectory, legendText);
file<<legend->scriptEntry();
}
file<<plotBasics.scriptTrailer();
} // end scriptfile
// run scriptfile
// --------------
logStatus<<"run scriptfile"<<Log::endl;
plotBasics.runScript();
}
catch(std::exception &e)
{
GROOPS_RETHROW(e)
}
}
/***********************************************/
|