Java source code of 'jhplot.Plot'

package jhplot;

import java.io.Serializable;

import jyplot.GraphicsUtil;
import jyplot.Jyplot;
import jyplot.JyplotVanilla;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.Writer;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import org.jfree.chart.axis.LogarithmicAxis;
import org.jfree.chart.axis.NumberAxis;
import org.jfree.chart.plot.Marker;
import org.jfree.chart.plot.ValueMarker;
import org.jfree.chart.plot.XYPlot;
import org.jfree.chart.renderer.xy.XYLineAndShapeRenderer;
import org.jfree.data.general.Dataset;
import org.jfree.data.xy.XYSeries;
import org.jfree.data.xy.XYSeriesCollection;
import org.jfree.ui.Layer;
import org.jfree.ui.LengthAdjustmentType;
import org.python.core.ArgParser;
import org.python.core.PyArray;
import org.python.core.PyFloat;
import org.python.core.PyInteger;
import org.python.core.PyList;
import org.python.core.PyObject;
import org.python.core.PyString;
import org.python.core.PyTuple;
import org.python.util.PythonInterpreter;

import jnumeric.JNumeric;

/**
 * Simple canvas to draw data. The syntax is similar to Matlab (actually
 * matplotlib). It is based on jFreeChart. For more advanced canvaces, look at
 * HPlot, HPlotJa.
 * 
 * Basic usage (in Jython):
 * 
 * 
 * j = Plot()
 * j.plot([1,2,3,2])
 * j.title("Some random plot")
 * j.show()
 * j.savefig(file='/tmp/someplot.png', 300, 200)
 * 
* * Plots can vbe saved in png,jpg,eps,svg, and pdf formats. *

* * * @author S.Chekanov. * */ public class Plot extends JyplotVanilla implements Serializable { /** * */ private static final long serialVersionUID = 1L; /** * Create a default canvas */ public Plot() { super(); } /** * For headless systems, there is the option of disabling the GUI. * * @param enableJFrame * true by default */ public Plot(boolean enableJFrame) { super(enableJFrame); } /** * Fast export of the canvas to an image file (depends on the extension, * i.e. PNG, JPG, PDF, EPS, SVG). No questions will be asked, an existing * file will be rewritten. * * @param file * Output file with the proper extension. If no extension, PNG * file is assumed. */ public void export(final String file) { savefig(file); } /** * Creates a horizontal line along the x axis. *

* Jython documentation * *

	 * 		axhline(y=0, xmin=0, xmax=1, **kwargs)
	 * 
*

* Draw a horizontal line at y from xmin to xmax. With the default values of * xmin=0 and xmax=1, this line will always span the horizontal extent of * the axes, regardless of the xlim settings, even if you change them, eg * with the xlim command. That is, the horizontal extent is in axes coords: * 0=left, 0.5=middle, 1.0=right but the y location is in data coordinates. *

* kwargs are the same as kwargs to plot (however so far only "linewidth" * and "color" are implemented) and can be used to control the line * properties. *

* Eg: *

* draw a thick red hline at y=0 that spans the xrange * *

	 * axhline(linewidth = 4, color = 'r')
	 * 
*

* draw a default hline at y=1 that spans the xrange * *

	 * axhline(y = 1)
	 * 
*

* draw a default hline at y=.5 that spans the the middle half of the xrange * *

	 * axhline(y = .5, xmin = 0.25, xmax = 0.75)
	 * 
*

* * @param args * Main arguments e.g. axhline(y=0, xmin=0, xmax=1 * @param kws * Additional arguments e.g. color='r', linewidth=4 */ public void axhline(float y, float xmin, float xmax, float linewidth, Color color) { // add a labelled marker Marker start = new ValueMarker(y); start.setStroke(new BasicStroke((float)linewidth)); start.setLabelOffsetType(LengthAdjustmentType.EXPAND); start.setPaint(color); // start.setLabel("Bid Start Price"); // start.setLabelAnchor(RectangleAnchor.BOTTOM_RIGHT); // start.setLabelTextAnchor(TextAnchor.TOP_RIGHT); this.getChart().getXYPlot().addRangeMarker(start, Layer.BACKGROUND); // Another possibility // add range marker for the cooling period... // Hour hour1 = new Hour(18, 30, 6, 2005); // Hour hour2 = new Hour(20, 30, 6, 2005); // double millis1 = hour1.getFirstMillisecond(); // double millis2 = hour2.getFirstMillisecond(); // Marker cooling = new IntervalMarker(millis1, millis2); // cooling.setLabelOffsetType(LengthAdjustmentType.EXPAND); // cooling.setPaint(new Color(150, 150, 255)); // cooling.setLabel("Automatic Cooling"); // cooling.setLabelFont(new Font("SansSerif", Font.PLAIN, 11)); // cooling.setLabelPaint(Color.blue); // cooling.setLabelAnchor(RectangleAnchor.TOP_LEFT); // cooling.setLabelTextAnchor(TextAnchor.TOP_RIGHT); // plot.addDomainMarker(cooling, Layer.BACKGROUND); } /** * Sets the figure size. *

* errorbar(x, y, yerr=None, xerr=None, fmt='b-', ecolor=None, capsize=3, * barsabove=False *

*/ public Dataset errorbar(double x[], double y[], double xerr[], double yerr[], String label, Color color, Color cerr, int capsize, boolean barsabove) { // System.out.println("errorbar()"); // // System.out.println("x: " + x); // System.out.println("y: " + y); // System.out.println("yerr: " + yerr); // System.out.println("xerr: " + xerr); // String colorstring = ap.getString(4, null); Color c = color; Color ec = cerr; return errorbar(x, y, xerr, yerr, ec, true); } /** * Sets the figure size. figure(num=None, figsize=None, dpi=None, * facecolor=None, edgecolor=None, frameon=True) */ public void figure(PyObject[] args, String[] kws) { ArgParser ap = new ArgParser("figure", args, kws, new String[] { "num", "figsize", "dpi", "facecolor", "edgecolor", "frameon" }); // plot.setBackgroundPaint(Color.lightGray); PyTuple figsizetuple = null; int dpi = ap.getInt(2, 150); try { figsizetuple = (PyTuple) ap.getPyObject(1, new PyTuple( new PyObject[] { new PyFloat(8), new PyFloat(6) })); // this.figureWidth = (float) ((PyFloat) // figsizetuple.get(0)).getValue(); // this.figureHeight = (float) ((PyFloat) // figsizetuple.get(1)).getValue(); this.dpi = dpi; } catch (Exception e) { System.out .println("Problem with figsize arguments. They should be in the form: (8,6)"); // e.printStackTrace(); } } private String[] getStringArrayFromPyList(PyObject list) { try { PyList plist = (PyList) list; String[] result = new String[plist.__len__()]; for (int i = 0; i < plist.__len__(); i++) { result[i] = plist.__getitem__(i).__str__().toString(); } return result; } catch (Exception e) { // e.printStackTrace(); return null; } } public void xticksa(PyObject[] args, String[] kws) { ArgParser ap = new ArgParser("xticks", args, kws, "locs", "labels"); double[] locs = getDoubleArrayFromPyList(ap.getPyObject(0)); String[] labels = getStringArrayFromPyList(ap.getPyObject(1, null)); /* Find out the size of the string array */ /* This is neccesary because of the limitations of the JFreechart */ double max = locs[0]; double min = locs[0]; for (int i = 0; i < locs.length; i++) { max = Math.max(max, locs[i]); min = Math.min(min, locs[i]); } int maxi = (int) max; int mini = (int) min; int sizeOfArray = maxi - mini; boolean generateLabelsFromLocations = false; if (labels == null) { labels = new String[sizeOfArray]; generateLabelsFromLocations = true; } else { } /* Set by default all labels as empty */ for (int i = 0; i < labels.length; i++) { labels[i] = new String(""); } /* * If there are no given labels, create them from the locations */ for (int i = 0; i < locs.length; i++) { } /* * Now generate the labels from the locations */ for (int i = 0, j = mini; i < locs.length; i++, j++) { if (generateLabelsFromLocations) { // labels = String.valueOf(locs); } } if (this.chart != null) { NumberAxis xNumberAxis = (NumberAxis) this.chart.getXYPlot() .getDomainAxis(); // SymbolAxis symbolaxis = new SymbolAxis(xNumberAxis.getLabel(), // ticklabels); // symbolaxis. // symbolaxis.setGridBandsVisible(false);//The grey bands that // suddnely appear // this.chart.getXYPlot().setDomainAxis(symbolaxis); } else System.out.println("xticks(): No chart created"); } public void setp(PyObject[] args, String[] kws) { ArgParser ap = new ArgParser("xticks", args, kws, "locs", "labels"); double[] locs = getDoubleArrayFromPyList(ap.getPyObject(0)); String[] labels = getStringArrayFromPyList(ap.getPyObject(1, null)); } double[] getDoubleArrayFromPyList(PyObject list) { if (list == null) { return null; } try { if (list instanceof PyList) { PyList plist = (PyList) list; double[] result = new double[plist.__len__()]; for (int i = 0; i < plist.__len__(); i++) { result[i] = plist.__getitem__(i).__float__().getValue(); } return result; } else if (list instanceof PyArray) { PyArray parray = (PyArray) list; double[] result = new double[parray.__len__()]; for (int i = 0; i < parray.__len__(); i++) { result[i] = parray.__getitem__(i).__float__().getValue(); } return result; } else if (list instanceof PyFloat) { PyFloat fl = (PyFloat) list; double[] result = new double[1]; result[0] = fl.getValue(); return result; } else if (list instanceof PyInteger) { PyInteger fl = (PyInteger) list; double[] result = new double[1]; result[0] = fl.getValue(); return result; /* else if (list instanceof JNumeric.PyMultiarray) { JNumeric.PyMultiarray parray = (JNumeric.PyMultiarray) list; double[] result = new double[parray.__len__()]; for (int i = 0; i < parray.__len__(); i++) { result[i] = parray.__getitem__(i).__float__().getValue(); } return result; */ } else { System.err.println("Failed to convert " + list.getClass().getName() + " into double[]"); } } catch (Exception e) { e.printStackTrace(); } return null; } /** * Sets the legend labels and location. * * @param args * @param kws */ public void legendtest(PyObject[] args, String[] kws) { System.out .println("legend(PyObject[] args, String[] kw) currently does not work"); // try // { // ArgParser ap = new ArgParser("legend", args, kws, new String[] // {"lines", "labels", "loc"}); // // String[] labels = getStringArrayFromPyList( ap.getPyObject(1, null) // ); // // if(labels != null) // { // // XYDataset[] datasets = new XYDataset[labels.length]; // // ArrayList datasets = new ArrayList(); // PyList pylist = (PyList)ap.getPyObject(0, null); // if(pylist != null) // { // for (Object object : pylist) // { // datasets.add((SeriesDataset)((PyObject)object).__tojava__(SeriesDataset.class)); // } // // for (int i = 0; i < datasets.size(); i++) // { // datasets.get(i)...getSeries(0).setKey(labels[i]); // } // } // // // // // } // // // } // catch (Exception e) // { // System.out.println("Problem with legend arguments."); // System.out.println("Try: legend( [dataset1,dataset2], ['Label1', 'label2']) "); // System.out.println("Or: legend( ['Label1', 'label2']) "); // // e.printStackTrace(); // } } /** * This assumes * * @param filename */ public void savefig(String filename) { int calculatedWidth = (int) this.figureWidth * this.dpi; int calculatedHeight = (int) this.figureHeight * this.dpi; if (calculatedWidth <= 0) calculatedWidth = 300; if (calculatedHeight <= 0) calculatedHeight = 200; this.savefig(filename, calculatedWidth, calculatedHeight); } public void savefig(String filename, int width, int height) { try { String fname = filename; String filetype = fname.substring(fname.length() - 3); if (filetype.equalsIgnoreCase("png")) { BufferedImage b = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g = b.createGraphics(); drawToGraphics2D(g, width, height); g.dispose(); ImageIO.write(b, "png", new File(fname)); } else if (filetype.equalsIgnoreCase("jpg") || filetype.equalsIgnoreCase("jpeg")) { BufferedImage b = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); Graphics2D g = b.createGraphics(); drawToGraphics2D(g, width, height); g.dispose(); ImageIO.write(b, "jpg", new File(fname)); } else if (filetype.equalsIgnoreCase("eps")) { try { FileOutputStream outputStream = new FileOutputStream(fname); org.jibble.epsgraphics.EpsGraphics2D g = new org.jibble.epsgraphics.EpsGraphics2D( "Example", outputStream, 0, 0, width, height);// #Create /** * * a new document with bounding box 0 0); Color color = getColorFromPyObject(ap.getPyObject(3, null)); /* * If the y axis is separate from the previous plots */ boolean separateYAxis = ap.getInt(5, 0) != 0; if (separateYAxis) setSeperateYAxis("", color, true); /* * This sets the colour of the series. */ setSeriesColor(color); /* * Make the series invisible in the legend if there is no label */ if (label == null || label.equalsIgnoreCase("")) { renderer.setSeriesVisibleInLegend(false); } this.chart.getXYPlot().configureDomainAxes();// #Needed to update the // plotted ranges; this.chart.getXYPlot().configureRangeAxes();// #Needed to update the // this.grid(false); return dataset; // plot(double[] x, double[] y, Color color, String dataname, double[] // yErrorBars, boolean seperateYAxis, boolean yaxisVisible, String // rangeLabel, boolean shapesVisible) // return plot(x, y, c, label, null, separateYAxis, true, null, // shapesVisible); } /** * Plot an arbitrary number of bars (boxes) * *

	 *  bar(left, height, width=0.8, bottom=0,
	 *      color=None, edgecolor=None, linewidth=None,
	 *      yerr=None, xerr=None, ecolor=None, capsize=3,
	 *      align='edge', orientation='vertical', log=False)
	 * 
* * Make a bar plot with rectangles bounded by left, left+width, bottom, * bottom+height (left, right, bottom and top edges) left, height, width, * and bottom can be either scalars or sequences. * * @param args * @param kws * @return A Dataset object */ public Dataset bar(PyObject[] args, String[] kws) { ArgParser ap = new ArgParser("bar", args, kws, new String[] { "left", "height", "width", "bottom", "color", "edgecolor", "linewidth", "yerr", "xerr", "ecolor", "capsize", "align", "orientation", "log" }); double[] left = getDoubleArrayFromPyList(ap.getPyObject(0, null)); double[] height = getDoubleArrayFromPyList(ap.getPyObject(1, null)); if (left == null) { System.err.println("left is null"); return null; } /* * If only a single array is given, assume it is the height, and create * for the left array [0, 1...n] */ if (height == null) { height = left; left = new double[height.length]; for (int i = 0; i < height.length; i++) { left[i] = i - 0.5; } } double[] width = getDoubleArrayFromPyList(ap.getPyObject(2, null)); if (width == null) { width = new double[height.length]; for (int i = 0; i < height.length; i++) { width[i] = 1; } } double[] bottom = getDoubleArrayFromPyList(ap.getPyObject(3, null)); if (bottom == null) { bottom = new double[height.length]; for (int i = 0; i < height.length; i++) { bottom[i] = 0; } } Color color = null; PyObject colorObject = ap.getPyObject(4, null); if (colorObject != null) { try { color = (Color) colorObject.__tojava__(Color.class); } catch (RuntimeException e) { // System.out.println("colorObject.getClass().getName():" + // colorObject.getClass().getName()); color = GraphicsUtil.getColorFromString(colorObject.toString()); } } return bar(left, height, width, bottom, color); } /** * Extract a java.awt.Color object from some PyObject. This could be a * string, a number, a tuple of numbers. *

* Single characters are converted to colors, as are strings that match * java.awt.Color fields, e.g. 'g' = green as does 'green'. */ private Color getColorFromPyObject(PyObject pyobject) { Color c = null; if (pyobject == null) { return null; } try { if (pyobject instanceof PyList) { PyList parray = (PyList) pyobject; int[] result = new int[parray.__len__()]; for (int i = 0; i < parray.__len__(); i++) { result[i] = (int) parray.__getitem__(i).__float__() .getValue(); } if (result.length == 1) { return new Color(result[0]); } else if (result.length == 3) { return new Color(result[0], result[1], result[2]); } else if (result.length == 4) { return new Color(result[0], result[1], result[2], result[3]); } } else if (pyobject instanceof PyArray) { PyArray parray = (PyArray) pyobject; int[] result = new int[parray.__len__()]; for (int i = 0; i < parray.__len__(); i++) { result[i] = (int) parray.__getitem__(i).__float__() .getValue(); } if (result.length == 1) { return new Color(result[0]); } else if (result.length == 3) { return new Color(result[0], result[1], result[2]); } else if (result.length == 4) { return new Color(result[0], result[1], result[2], result[3]); } } else if (pyobject instanceof PyString) { return getColor(pyobject.toString()); } // TODO: fix else if (Class.forName("java.awt.Color").isInstance(pyobject)) // || // pyobject.__class__.__name__.equals("java.awt.Color") { return ((Color) pyobject.__tojava__(Color.class)); /*if (pyobject instanceof JNumeric.PyMultiarray) { JNumeric.PyMultiarray parray = (JNumeric.PyMultiarray) pyobject; int[] result = new int[parray.__len__()]; for (int i = 0; i < parray.__len__(); i++) { result[i] = (int) parray.__getitem__(i).__float__() .getValue(); } if (result.length == 1) { return new Color(result[0]); } else if (result.length == 3) { return new Color(result[0], result[1], result[2]); } else if (result.length == 4) { return new Color(result[0], result[1], result[2], result[3]); } */ } else { // TODO: fix // logger.warn("Failed to convert " + pyobject.toString() + "( " // + pyobject.getClass().getName() + ", " + // pyobject.__class__.__name__ + ") into A Color constructor"); } } catch (Exception e) { e.printStackTrace(); } return c; } /** * Make a semilog plot with log scaling on the x axis. The args to semilog x * are the same as the args to plot. See help plot for more info. Optional * keyword args supported are any of the kwargs supported by plot or * set_xscale. Notable, for log scaling: basex: base of the logarithm subsx: * the location of the minor ticks; None defaults to autosubs, which depend * on the number of decades in the plot; see set_xscale for details */ public Dataset semilogx(PyObject[] args, String[] kws) { ArgParser ap = new ArgParser("semilogx", args, kws, new String[] { "basex", "subsx" }); /* * This part is not used yet double basex = 10; PyObject _basex = * ap.getPyObject(0, null); if(_basex != null && _basex instanceof * PyFloat) { PyFloat fl = (PyFloat) _basex; basex = fl.getValue(); } */ Dataset dataset = this.plot(args, kws); XYPlot plot = (XYPlot) chart.getPlot(); String xlabel = plot.getDomainAxis().getLabel(); plot.setDomainAxis(new LogarithmicAxis(xlabel)); return dataset; } public Dataset semilogy(PyObject[] args, String[] kws) { ArgParser ap = new ArgParser("semilogy", args, kws, new String[] { "basex", "subsx" }); /* * This part is not used yet double basex = 10; PyObject _basex = * ap.getPyObject(0, null); if(_basex != null && _basex instanceof * PyFloat) { PyFloat fl = (PyFloat) _basex; basex = fl.getValue(); } */ Dataset dataset = this.plot(args, kws); XYPlot plot = (XYPlot) chart.getPlot(); String ylabel = plot.getRangeAxis().getLabel(); plot.setRangeAxis(new LogarithmicAxis(ylabel)); return dataset; } public Dataset loglog(PyObject[] args, String[] kws) { // ArgParser ap = new ArgParser("semilogx", args, kws, new // String[]{"basex", "subsx"}); /* * This part is not used yet double basex = 10; PyObject _basex = * ap.getPyObject(0, null); if(_basex != null && _basex instanceof * PyFloat) { PyFloat fl = (PyFloat) _basex; basex = fl.getValue(); } */ Dataset dataset = this.plot(args, kws); /* Remove items equal or below zero */ ArrayList xValuesToRemove = new ArrayList(); for (int i = 0; i < ((XYSeriesCollection) dataset).getSeriesCount(); i++) { XYSeries series = ((XYSeriesCollection) dataset).getSeries(i); xValuesToRemove.clear(); for (int j = 0; j < series.getItemCount(); j++) { if (series.getX(j).doubleValue() <= 0 || series.getY(j).doubleValue() <= 0) { xValuesToRemove.add(series.getX(j)); } } for (Number number : xValuesToRemove) { series.remove(number); } } XYPlot plot = (XYPlot) chart.getPlot(); String ylabel = plot.getRangeAxis().getLabel(); plot.setRangeAxis(new LogarithmicAxis(ylabel)); String xlabel = plot.getDomainAxis().getLabel(); plot.setDomainAxis(new LogarithmicAxis(xlabel)); return dataset; } public static void main(String[] args) { /* Testing */ /* Log plots */ PythonInterpreter python = new PythonInterpreter(); Jyplot p = new Jyplot(); PyObject[] x = { new PyFloat(0.1), new PyFloat(1), new PyFloat(10), new PyFloat(100), new PyFloat(1000) }; PyObject[] y = { new PyFloat(10), new PyFloat(6), new PyFloat(260), new PyFloat(140), new PyFloat(15000) }; PyList xx = new PyList(x); PyList yy = new PyList(y); p.loglog(new PyObject[] { xx, yy }, null); p.show(); p.savefig("a.pdf", 300, 200); } /** * Plot Y vs X. Same as plot(x,y). * @param x X values * @param y Y vlaues */ public void draw(double[] x, double[] y){ plot(x,y); } /** * Plot P1D object. Assume two dimensions. No errors. * @param p input P1D */ public void draw(P1D p){ plot(p.getArrayX(),p.getArrayY()); } /** * Plot F1D function. Min and Max values should be defined * during the function initialisation. * @param f1 * @param min * @param max */ public void draw(F1D f1, double min, double max){ f1.eval(min,max,f1.getPoints()); // evaluate first. plot(f1.getArrayX(),f1.getArrayY()); } /** * Is domain logarithmic * @return */ private boolean isDomainLogarithmic() { if (this.chart == null) return false; if (this.chart.getXYPlot().getDomainAxis().getClass().getName() .contains("Logarithmic")) return true; return false; } /** * Is range logarithmic * @return */ private boolean isRangeLogarithmic() { if (this.chart == null) return false; if (this.chart.getXYPlot().getRangeAxis().getClass().getName() .contains("Logarithmic")) return true; return false; } }