Skip to content

Commit

Permalink
ttools: add example code for programmatic use of new STILTS plots
Browse files Browse the repository at this point in the history
A new class ttools.example.SinePlot runs an animated live plot in a
window.  It comes with a couple of associated source files that give
two alternative ways to set the plot up.

All commented in what I hope is a didactic fasion.
  • Loading branch information
mbtaylor authored and mmpcn committed Nov 27, 2014
1 parent e2c02a3 commit 0f4b0c1
Show file tree
Hide file tree
Showing 3 changed files with 450 additions and 0 deletions.
164 changes: 164 additions & 0 deletions ttools/src/main/uk/ac/starlink/ttools/example/ApiPlanePlotter.java
@@ -0,0 +1,164 @@
package uk.ac.starlink.ttools.example;

import java.awt.Color;
import java.awt.Dimension;
import java.io.IOException;
import javax.swing.Icon;
import javax.swing.JComponent;
import uk.ac.starlink.table.StarTable;
import uk.ac.starlink.ttools.plot.MarkShape;
import uk.ac.starlink.ttools.plot.Range;
import uk.ac.starlink.ttools.plot2.BasicCaptioner;
import uk.ac.starlink.ttools.plot2.Captioner;
import uk.ac.starlink.ttools.plot2.DataGeom;
import uk.ac.starlink.ttools.plot2.Navigator;
import uk.ac.starlink.ttools.plot2.PlotLayer;
import uk.ac.starlink.ttools.plot2.ShadeAxis;
import uk.ac.starlink.ttools.plot2.config.StyleKeys;
import uk.ac.starlink.ttools.plot2.data.DataSpec;
import uk.ac.starlink.ttools.plot2.data.DataStore;
import uk.ac.starlink.ttools.plot2.data.DataStoreFactory;
import uk.ac.starlink.ttools.plot2.data.SimpleDataStoreFactory;
import uk.ac.starlink.ttools.plot2.geom.PlaneAspect;
import uk.ac.starlink.ttools.plot2.geom.PlaneNavigator;
import uk.ac.starlink.ttools.plot2.geom.PlanePlotType;
import uk.ac.starlink.ttools.plot2.geom.PlaneSurfaceFactory;
import uk.ac.starlink.ttools.plot2.layer.MarkForm;
import uk.ac.starlink.ttools.plot2.layer.Outliner;
import uk.ac.starlink.ttools.plot2.layer.ShapeMode;
import uk.ac.starlink.ttools.plot2.layer.ShapePlotter;
import uk.ac.starlink.ttools.plot2.layer.ShapeStyle;
import uk.ac.starlink.ttools.plot2.layer.Stamper;
import uk.ac.starlink.ttools.plot2.paper.Compositor;
import uk.ac.starlink.ttools.plot2.task.ColumnDataSpec;
import uk.ac.starlink.ttools.plot2.task.PlotDisplay;

/**
* PlanePlotter implementation that sets up a plot explicitly.
* There's a lot to do, it's quite complicated, even for a simple plot.
* If you don't like this approach, see the much more straightforward
* {@link EnvPlanePlotter} implementation instead.
* This approach however gives compile-time type-checking of the
* plot parameters.
*
* @author Mark Taylor
* @since 12 Jun 2014
*/
public class ApiPlanePlotter implements SinePlot.PlanePlotter {

public JComponent createPlotComponent( StarTable table,
boolean dataMayChange )
throws InterruptedException, IOException {

/* It's a 2d plot. */
PlanePlotType plotType = PlanePlotType.getInstance();
DataGeom geom = plotType.getPointDataGeoms()[ 0 ];

/* Create the Profile for the plot surface. This encapsulates
* those things about the geometry and appearance of the plot
* axes which will not change with window resizing, zooming etc. */
PlaneSurfaceFactory surfFact = new PlaneSurfaceFactory();
boolean xlog = false;
boolean ylog = false;
boolean xflip = false;
boolean yflip = false;
String xlabel = "X axis";
String ylabel = "Y axis";
Captioner captioner = new BasicCaptioner();
double xyfactor = Double.NaN;
boolean grid = false;
double xcrowd = 1;
double ycrowd = 1;
boolean minor = true;
Color gridColor = Color.BLACK;
Color axlabelColor = Color.BLACK;
PlaneSurfaceFactory.Profile profile =
new PlaneSurfaceFactory.Profile( xlog, ylog, xflip, yflip,
xlabel, ylabel, captioner,
xyfactor, grid, xcrowd, ycrowd,
minor, gridColor, axlabelColor );

/* Set up a plot Aspect. This is the initial data range,
* and is subject to change by user navigation. */
double[] xlimits = new double[] { 0, 1 };
double[] ylimits = new double[] { -1.2, 1.2 };
PlaneAspect aspect = new PlaneAspect( xlimits, ylimits );

/* Set up a Navigator which determines what mouse gestures are
* available to the user for plot pan/zoom etc. Note that
* anisotropic pan/zoom are available with wheel/drag gestures
* outside the plot axes. */
double zoomFactor = StyleKeys.ZOOM_FACTOR.getDefaultValue();
boolean xZoom = true;
boolean yZoom = true;
boolean xPan = true;
boolean yPan = true;
double xAnchor = Double.NaN;
double yAnchor = Double.NaN;
Navigator<PlaneAspect> navigator =
new PlaneNavigator( zoomFactor,
xZoom, yZoom, xPan, yPan, xAnchor, yAnchor );

/* We will not use optional decorations for this plot. */
Icon legend = null;
float[] legPos = null;
ShadeAxis shadeAxis = null;
Range shadeFixRange = null;
boolean surfaceAuxRange = false;

/* Prepare the list of plot layers; in this case there is only one. */
PlotLayer[] layers = { createScatterLayer( geom, table), };

/* Prepare the data cache. */
int nl = layers.length;
DataSpec[] dataSpecs = new DataSpec[ nl ];
for ( int il = 0; il < nl; il++ ) {
dataSpecs[ il ] = layers[ il ].getDataSpec();
}
DataStoreFactory storeFact = new SimpleDataStoreFactory();
DataStore dataStore = storeFact.readDataStore( dataSpecs, null );
boolean caching = ! dataMayChange;

/* Finally construct, size and return the plot component. */
Compositor compositor = Compositor.SATURATION;
JComponent comp =
new PlotDisplay<PlaneSurfaceFactory.Profile,PlaneAspect>
( plotType, layers, surfFact, profile, aspect,
legend, legPos, shadeAxis, shadeFixRange,
dataStore, surfaceAuxRange, navigator,
compositor, caching );
comp.setPreferredSize( new Dimension( 500, 400 ) );
return comp;
}

/**
* Returns a plot layer plotting the first two columns of a given table
* against each other.
*
* @param geom data geom
* @param table data table
* @return new layer
*/
private PlotLayer createScatterLayer( DataGeom geom, StarTable table ) {

/* Prepare the data for the scatter plot layer: use the first
* two columns of the supplied table as X and Y.*/
DataSpec dataSpec =
new ColumnDataSpec( table, geom.getPosCoords(),
new int[][] { { 0 }, { 1 } } );

/* Prepare the graphical style of the scatter plot layer:
* it's a scatter plot with single-position markers, plotted
* in a single fixed colour. */
ShapePlotter plotter =
ShapePlotter.createFlat2dPlotter( MarkForm.SINGLE );
MarkShape shape = MarkShape.OPEN_CIRCLE;
int size = 2;
Outliner outliner = MarkForm.createMarkOutliner( shape, size );
Stamper stamper = new ShapeMode.FlatStamper( Color.RED );
ShapeStyle style = new ShapeStyle( outliner, stamper );

/* Combine the data and style to generate a plot layer. */
return plotter.createLayer( geom, dataSpec, style );
}
}
74 changes: 74 additions & 0 deletions ttools/src/main/uk/ac/starlink/ttools/example/EnvPlanePlotter.java
@@ -0,0 +1,74 @@
package uk.ac.starlink.ttools.example;

import java.io.IOException;
import javax.swing.JComponent;
import uk.ac.starlink.table.StarTable;
import uk.ac.starlink.task.TaskException;
import uk.ac.starlink.ttools.plot2.task.Plot2Task;
import uk.ac.starlink.ttools.task.MapEnvironment;

/**
* PlanePlotter implementation that uses the name/value pairs in the
* same way as the STILTS application command-line interface to set
* up a plot.
*
* This is much easier to do than the alternative, since the large
* majority of the options will assume sensible defaults if not set.
* It allows pretty much the same capabilities. However, it does
* not offer compile-time safety: there is no guarantee that a plot
* set up like this will not generate a run-time error.
*
* @author Mark Taylor
* @since 12 Jun 2014
*/
public class EnvPlanePlotter implements SinePlot.PlanePlotter {
public JComponent createPlotComponent( StarTable table,
boolean dataMayChange )
throws InterruptedException, IOException, TaskException {

/* Create an execution environment for the stilts plot task. */
MapEnvironment env = new MapEnvironment();

/* Populate the environment with parameter name/value pairs.
* For the available parameters and their values, see the user
* documentation of the corresponding STILTS command-line task.
* At time of writing, this documentation does not exist :-[.
*
* Some general points:
*
* - In most cases the values are strings.
*
* - For some parameters non-String objects of a relevant type
* are also allowed. In particular parameters accepting
* tables will take StarTable objects.
*
* - Most parameters are optional, and will assume sensible
* defaults if not set. There are several tens of parameters
* available, allowing detailed setup if you want to do it.
* In the example below, the required parameters are so marked,
* the others can be omitted if you want to accept default values.
*/

/* Global parameters for the plot. */
env.setValue( "type", "plane" ); // required
env.setValue( "insets", "10,30,30,8" );

/* Parameters for the first (in this case, only) layer;
* the parameter names have a trailing (arbitrary) label "1".
* The values of the x1/y1 parameters, giving the data coordinates,
* are names of the columns in the input table. */
env.setValue( "layer1", "mark-flat" ); // required
env.setValue( "in1", table ); // required
env.setValue( "x1", "x" ); // required
env.setValue( "y1", "y" ); // required
env.setValue( "shape1", "open circle" );
env.setValue( "size1", "2" );

/* You could add more layers here. */

/* Pass the populated environment to the Plot2Task object,
* which can turn it into a JComponent containing the plot. */
boolean caching = ! dataMayChange;
return new Plot2Task().createPlotComponent( env, caching );
}
}

0 comments on commit 0f4b0c1

Please sign in to comment.