/*
* Copyright (c) 2012 Diamond Light Source Ltd.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*/
package uk.ac.diamond.scisoft.analysis;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.eclipse.dawnsci.analysis.api.io.IDataHolder;
import org.eclipse.dawnsci.analysis.api.io.ScanFileHolderException;
import org.eclipse.dawnsci.analysis.api.tree.Tree;
import org.eclipse.january.dataset.CompoundDataset;
import org.eclipse.january.dataset.Dataset;
import org.eclipse.january.dataset.DatasetFactory;
import org.eclipse.january.dataset.DatasetUtils;
import org.eclipse.january.dataset.IDataset;
import org.eclipse.january.dataset.RGBDataset;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import uk.ac.diamond.scisoft.analysis.io.DataHolder;
import uk.ac.diamond.scisoft.analysis.io.LoaderFactory;
import uk.ac.diamond.scisoft.analysis.io.RawBinarySaver;
import uk.ac.diamond.scisoft.analysis.plotserver.AxisMapBean;
import uk.ac.diamond.scisoft.analysis.plotserver.AxisOperation;
import uk.ac.diamond.scisoft.analysis.plotserver.DataBean;
import uk.ac.diamond.scisoft.analysis.plotserver.DataBeanException;
import uk.ac.diamond.scisoft.analysis.plotserver.DatasetWithAxisInformation;
import uk.ac.diamond.scisoft.analysis.plotserver.GuiBean;
import uk.ac.diamond.scisoft.analysis.plotserver.GuiParameters;
import uk.ac.diamond.scisoft.analysis.plotserver.GuiPlotMode;
/**
* Normal implementation of {@link ISDAPlotter} used by delegator class {@link SDAPlotter}
*/
public class SDAPlotterImpl implements ISDAPlotter {
private static final Logger logger = LoggerFactory.getLogger(SDAPlotterImpl.class);
private static ISDAPlotter instance;
/**
* Get the default or "normal" instance of the SDAPlotter to use.
* @return instance of SDAPlotter
*/
public synchronized static ISDAPlotter getDefaultInstance() {
if (instance == null) {
instance = new SDAPlotterImpl();
}
return instance;
}
/**
* All locations within SDAPlotter that need a PlotService should get it with this method. This method will collect
* a PlotService from the provider.
* <p>
* Note this method exists to funnel all accesses to the PlotService via here, its purpose is to allow subclasses of
* SDAPlotterImpl to provide their own PlotService and keep all dependencies on a static method calls to one place.
*
* @return PlotService
*/
protected PlotService getPlotService() {
return PlotServiceProvider.getPlotService();
}
/**
* Not strictly private to enable other implementation of ISDAPlotter to
* extend this one, such as versions used in test.
*/
protected SDAPlotterImpl() {
}
private boolean isDataND(IDataset data, int dim) {
return data.getRank() == dim;
}
static IDataset[] validateXValues(final IDataset xValues, final IDataset... yValues) {
if (xValues == null) {
if (yValues == null || yValues.length == 0) {
logger.error("No datasets specified");
throw new IllegalArgumentException("No datasets specified");
}
int max = 0;
for (IDataset y : yValues) {
if (y != null) {
int s = y.getSize();
if (s > max)
max = s;
}
}
return new IDataset[] { DatasetFactory.createRange(max, Dataset.INT32) };
}
return new IDataset[] { xValues };
}
static IDataset[] validateAllXValues(final IDataset[] xValues, final IDataset... yValues) {
if (xValues == null) {
return validateXValues(null, yValues);
}
return xValues;
}
@Override
public void plot(String plotName, final String title, IDataset[] xValues, IDataset[] yValues, final String[] yLabels, final String[] xAxisNames, final String[] yAxisNames) throws Exception {
lplot(plotName, title, validateAllXValues(xValues, yValues), yValues, yLabels, xAxisNames, yAxisNames, GuiParameters.PLOTOP_NONE);
}
@Override
public void addPlot(String plotName, final String title, IDataset[] xValues, IDataset[] yValues, final String[] yLabels, final String[] xAxisNames, final String[] yAxisNames) throws Exception {
lplot(plotName, title, validateAllXValues(xValues, yValues), yValues, yLabels, xAxisNames, yAxisNames, GuiParameters.PLOTOP_ADD);
}
@Override
public void updatePlot(String plotName, final String title, IDataset[] xValues, IDataset[] yValues, final String xAxisName, final String yAxisName) throws Exception {
lplot(plotName, title, validateAllXValues(xValues, yValues), yValues, null, new String[] {xAxisName}, new String[] {yAxisName}, GuiParameters.PLOTOP_UPDATE);
}
private final static String EMPTY = "";
/**
* Plot line(s) in named view
* @param plotName
* @param title (can be null)
* @param xValues
* @param yValues
* @param yLabels (can be null)
* @param xAxisNames (can be null)
* @param yAxisNames (can be null)
* @param plotOperation one of GuiParameters.PLOTOP_*
* @throws Exception
*/
private void lplot(final String plotName, final String title, IDataset[] xValues, IDataset[] yValues, final String[] yLabels, final String[] xAxisNames, final String[] yAxisNames, final String plotOperation) throws Exception {
if (yValues.length == 0) {
return;
}
for (IDataset x : xValues) {
if (!isDataND(x, 1)) {
logger.error("Input x dataset has incorrect rank: it has {} dimensions when it should be 1",
x.getRank());
throw new Exception("Input x dataset has incorrect rank: it should be 1");
}
}
for (IDataset y : yValues) {
if (!isDataND(y, 1)) {
logger.error("Input y dataset has incorrect rank: it has {} dimensions when it should be 1",
y.getRank());
throw new Exception("Input y dataset has incorrect rank: it should be 1");
}
}
if (yLabels != null && yLabels.length != yValues.length) {
logger.error("Number of y labels ({}) should match number of y datasets ({})", yLabels.length, yValues.length);
throw new Exception("Number of y labels should match number of y datasets");
}
logger.info("Plot sent to {}", plotName);
// Create the beans to transfer the data
DataBean dataBean = new DataBean(GuiPlotMode.ONED);
if (xValues.length == 1) {
String xid = AxisMapBean.XAXIS;
String xan = null;
if (xAxisNames != null && xAxisNames.length > 0) {
xan = xAxisNames[0];
xValues[0].setName(EMPTY); // empty dataset name to avoid confusion
} else {
xan = AxisMapBean.XAXIS;
}
dataBean.addAxis(xid, xValues[0]);
for (int i = 0; i < yValues.length; i++) {
try {
String yan = null;
if (yAxisNames != null && yAxisNames.length >= yValues.length) {
yan = yAxisNames[i];
} else {
yan = AxisMapBean.YAXIS;
}
IDataset yd = renameDataset(yValues[i], yLabels == null ? null : yLabels[i]);
dataBean.addData(DatasetWithAxisInformation.createAxisDataSet(yd, new String[] {xid}, new String[] {xan, yan}));
} catch (DataBeanException e) {
logger.error("Problem adding data to bean as axis key does not exist");
e.printStackTrace();
}
}
} else {
if (xValues.length != yValues.length)
throw new IllegalArgumentException("# xValues does not match # yValues");
Map<String, IDataset> cache = new HashMap<String, IDataset>();
int l = 0; // last axis number
for (int i = 0; i < xValues.length; i++) {
IDataset x = xValues[i];
String xid = null;
for (String s : cache.keySet()) {
if (cache.get(s) == x) {
xid = s;
break;
}
}
if (xid == null) {
xid = l == 0 ? AxisMapBean.XAXIS : AxisMapBean.XAXIS + l;
l++;
cache.put(xid, x);
}
String xan = null;
if (xAxisNames != null) {
xan = xAxisNames.length >= xValues.length ? xAxisNames[i] : xAxisNames[0];
x.setName(EMPTY);
} else {
xan = AxisMapBean.XAXIS; // single axis
}
String yan = null;
if (yAxisNames != null && yAxisNames.length >= yValues.length) {
yan = yAxisNames[i];
} else {
yan = AxisMapBean.YAXIS; // single axis
}
IDataset yd = renameDataset(yValues[i], yLabels == null ? null : yLabels[i]);
dataBean.addAxis(xid, x);
// now add it to the plot data
try {
dataBean.addData(DatasetWithAxisInformation.createAxisDataSet(yd, new String[] {xid}, new String[] {xan, yan}));
} catch (DataBeanException e) {
logger.error("Problem adding data to bean as axis key does not exist");
e.printStackTrace();
}
}
cache.clear();
}
dataBean.putGuiParameter(GuiParameters.PLOTOPERATION, plotOperation);
if (title != null)
dataBean.putGuiParameter(GuiParameters.TITLE, title);
setDataBean(plotName, dataBean);
}
private static IDataset renameDataset(IDataset d, String n) {
if (n != null && n.length() > 0) {
d = d.clone();
d.setName(n);
}
return d;
}
@Override
public void imagePlot(String plotName, String imageFileName) throws Exception {
IDataHolder dh = null;
try {
// This is the bit that can take a while.
dh = LoaderFactory.getData(imageFileName, null);
} catch (Exception ne) {
logger.error("Cannot load file " + imageFileName, ne);
throw ne;
}
try {
IDataset dataSet = dh.getDataset(0);
dataSet.setName(imageFileName);
imagePlot(plotName, null, null, dataSet, null, null);
} catch (Exception e) {
logger.error("Cannot plot non-image file from " + imageFileName, e);
throw e;
}
}
@Override
public void imagePlot(String plotName, IDataset xValues, IDataset yValues, IDataset image, String xAxisName, String yAxisName) throws Exception {
if (isDataND(image, 3) && image.getShape()[2] == 3) { // hack for RGB ndarrays from python
CompoundDataset compound = DatasetUtils.createCompoundDatasetFromLastAxis(DatasetUtils.convertToDataset(image), true);
image = RGBDataset.createFromCompoundDataset(compound);
}
if (!isDataND(image, 2)) {
logger.error("Input dataset has incorrect rank: it has {} dimensions when it should be 2", image.getRank());
throw new Exception("Input dataset has incorrect rank: it should be 2");
}
if (xValues != null && !isDataND(xValues, 1)) {
String msg = String.format("X axis dataset has incorrect rank: it has %d dimensions when it should be 1",
xValues.getRank());
logger.error(msg);
throw new Exception(msg);
}
if (yValues != null && !isDataND(yValues, 1)) {
String msg = String.format("Y axis dataset has incorrect rank: it has %d dimensions when it should be 1",
yValues.getRank());
logger.error(msg);
throw new Exception(msg);
}
DataBean dataBean = new DataBean(GuiPlotMode.TWOD);
DatasetWithAxisInformation axisData = new DatasetWithAxisInformation();
AxisMapBean amb = new AxisMapBean();
axisData.setAxisMap(amb);
axisData.setData(image);
try {
dataBean.addData(axisData);
} catch (DataBeanException e) {
e.printStackTrace();
}
if (xValues != null) {
IDataset xd = renameDataset(xValues, xAxisName);
dataBean.addAxis(AxisMapBean.XAXIS, xd);
}
if (yValues != null) {
IDataset yd = renameDataset(yValues, yAxisName);
dataBean.addAxis(AxisMapBean.YAXIS, yd);
}
setDataBean(plotName, dataBean);
}
@Override
public void imagesPlot(String plotName, IDataset xValues, IDataset yValues, IDataset[] images) throws Exception {
if (!isDataND(images[0], 2)) {
logger.error("Input dataset has incorrect rank: it has {} dimensions when it should be 2",
images[0].getRank());
throw new Exception("Input dataset has incorrect rank: it should be 2");
}
if (xValues != null && !isDataND(xValues, 1)) {
String msg = String.format("X axis dataset has incorrect rank: it has %d dimensions when it should be 1",
xValues.getRank());
logger.error(msg);
throw new Exception(msg);
}
if (yValues != null && !isDataND(yValues, 1)) {
String msg = String.format("Y axis dataset has incorrect rank: it has %d dimensions when it should be 1",
yValues.getRank());
logger.error(msg);
throw new Exception(msg);
}
DataBean dataBean = new DataBean(GuiPlotMode.MULTI2D);
for (int i = 0; i < images.length; i++) {
DatasetWithAxisInformation axisData = new DatasetWithAxisInformation();
AxisMapBean amb = new AxisMapBean();
axisData.setAxisMap(amb);
axisData.setData(images[i]);
try {
dataBean.addData(axisData);
} catch (DataBeanException e) {
e.printStackTrace();
}
}
if (xValues != null) {
dataBean.addAxis(AxisMapBean.XAXIS, xValues);
}
if (yValues != null) {
dataBean.addAxis(AxisMapBean.YAXIS, yValues);
}
setDataBean(plotName, dataBean);
}
@Override
public void scatter2DPlot(String plotName, CompoundDataset[] coordPairs, IDataset[] sizes)
throws Exception {
if (coordPairs.length != sizes.length) {
String msg = String.format("# of coordPairs does not match # of sizes (%d != %d)", coordPairs.length,
sizes.length);
logger.error(msg);
throw new Exception(msg);
}
DataBean dataBean = new DataBean(GuiPlotMode.SCATTER2D);
dataBean.putGuiParameter(GuiParameters.PLOTOPERATION, GuiParameters.PLOTOP_NONE);
for (int i = 0; i < sizes.length; i++) {
CompoundDataset coordData = coordPairs[i];
if (coordData.getElementsPerItem() != 2) {
String msg = String.format("# of elements of coordPair does not equal two it is %d",
coordData.getElementsPerItem());
logger.error(msg);
throw new Exception(msg);
}
if (coordData.getShape().length != 1) {
String msg = String.format("shape of coordpair does not match (expected one but got %d)",
coordData.getShape().length);
logger.error(msg);
throw new Exception(msg);
}
Dataset xCoord = coordData.getElements(0);
Dataset yCoord = coordData.getElements(1);
DatasetWithAxisInformation axisData = new DatasetWithAxisInformation();
AxisMapBean amb = new AxisMapBean();
axisData.setAxisMap(amb);
axisData.setData(sizes[i]);
dataBean.addData(axisData);
dataBean.addAxis(AxisMapBean.XAXIS + Integer.toString(i), xCoord);
dataBean.addAxis(AxisMapBean.YAXIS + Integer.toString(i), yCoord);
}
setDataBean(plotName, dataBean);
}
@Override
public void scatter2DPlot(String plotName, IDataset xCoords, IDataset yCoords, IDataset sizes)
throws Exception {
if (xCoords != null && !isDataND(xCoords, 1)) {
String msg = String.format("X coords dataset has incorrect rank: it has %d dimensions when it should be 1",
xCoords.getRank());
logger.error(msg);
throw new Exception(msg);
}
if (yCoords != null && !isDataND(yCoords, 1)) {
String msg = String.format("Y coords dataset has incorrect rank: it has %d dimensions when it should be 1",
yCoords.getRank());
logger.error(msg);
throw new Exception(msg);
}
DataBean dataBean = new DataBean(GuiPlotMode.SCATTER2D);
dataBean.putGuiParameter(GuiParameters.PLOTOPERATION, GuiParameters.PLOTOP_NONE);
DatasetWithAxisInformation axisData = new DatasetWithAxisInformation();
AxisMapBean amb = new AxisMapBean();
axisData.setAxisMap(amb);
axisData.setData(sizes);
try {
dataBean.addData(axisData);
} catch (DataBeanException e) {
e.printStackTrace();
}
if (xCoords != null) {
dataBean.addAxis(AxisMapBean.XAXIS + "0", xCoords);
}
if (yCoords != null) {
dataBean.addAxis(AxisMapBean.YAXIS + "0", yCoords);
}
setDataBean(plotName, dataBean);
}
// temporary(?) fix for plotting over too quickly when some additional points are omitted
private static final int REST_BETWEEN_PLOTOVERS = 6;
@Override
public void scatter2DPlotOver(String plotName, IDataset xCoords, IDataset yCoords, IDataset sizes)
throws Exception {
if (xCoords != null && !isDataND(xCoords, 1) || xCoords == null) {
String msg = String.format("X coords dataset has incorrect rank or is null");
logger.error(msg);
throw new Exception(msg);
}
if (yCoords != null && !isDataND(yCoords, 1) || yCoords == null) {
String msg = String.format("Y coords dataset has incorrect rank or is null");
logger.error(msg);
throw new Exception(msg);
}
Thread.sleep(REST_BETWEEN_PLOTOVERS);
DataBean dataBean = new DataBean(GuiPlotMode.SCATTER2D);
DatasetWithAxisInformation axisData = new DatasetWithAxisInformation();
AxisMapBean amb = new AxisMapBean();
axisData.setAxisMap(amb);
dataBean.putGuiParameter(GuiParameters.PLOTOPERATION, GuiParameters.PLOTOP_UPDATE);
axisData.setData(sizes);
try {
dataBean.addData(axisData);
} catch (DataBeanException e) {
e.printStackTrace();
}
dataBean.addAxis(AxisMapBean.XAXIS + "0", xCoords);
dataBean.addAxis(AxisMapBean.YAXIS + "0", yCoords);
setDataBean(plotName, dataBean);
}
@Override
public void scatter3DPlot(String plotName, IDataset xCoords, IDataset yCoords, IDataset zCoords,
IDataset sizes) throws Exception {
if (xCoords != null && !isDataND(xCoords, 1)) {
String msg = String.format("X coords dataset has incorrect rank: it has %d dimensions when it should be 1",
xCoords.getRank());
logger.error(msg);
throw new Exception(msg);
}
if (yCoords != null && !isDataND(yCoords, 1)) {
String msg = String.format("Y coords dataset has incorrect rank: it has %d dimensions when it should be 1",
yCoords.getRank());
logger.error(msg);
throw new Exception(msg);
}
if (zCoords != null && !isDataND(zCoords, 1)) {
String msg = String.format("Z coords dataset has incorrect rank: it has %d dimensions when it should be 1",
zCoords.getRank());
logger.error(msg);
throw new Exception(msg);
}
DataBean dataBean = new DataBean(GuiPlotMode.SCATTER3D);
dataBean.putGuiParameter(GuiParameters.PLOTOPERATION, GuiParameters.PLOTOP_NONE);
DatasetWithAxisInformation axisData = new DatasetWithAxisInformation();
AxisMapBean amb = new AxisMapBean();
axisData.setAxisMap(amb);
axisData.setData(sizes);
try {
dataBean.addData(axisData);
} catch (DataBeanException e) {
e.printStackTrace();
}
if (xCoords != null) {
dataBean.addAxis(AxisMapBean.XAXIS, xCoords);
}
if (yCoords != null) {
dataBean.addAxis(AxisMapBean.YAXIS, yCoords);
}
if (zCoords != null) {
dataBean.addAxis(AxisMapBean.ZAXIS, zCoords);
}
setDataBean(plotName, dataBean);
}
@Override
public void scatter3DPlotOver(String plotName, IDataset xCoords, IDataset yCoords, IDataset zCoords,
IDataset sizes) throws Exception {
if (xCoords != null && !isDataND(xCoords, 1)) {
String msg = String.format("X coords dataset has incorrect rank: it has %d dimensions when it should be 1",
xCoords.getRank());
logger.error(msg);
throw new Exception(msg);
}
if (yCoords != null && !isDataND(yCoords, 1)) {
String msg = String.format("Y coords dataset has incorrect rank: it has %d dimensions when it should be 1",
yCoords.getRank());
logger.error(msg);
throw new Exception(msg);
}
if (zCoords != null && !isDataND(zCoords, 1)) {
String msg = String.format("Z coords dataset has incorrect rank: it has %d dimensions when it should be 1",
zCoords.getRank());
logger.error(msg);
throw new Exception(msg);
}
Thread.sleep(REST_BETWEEN_PLOTOVERS);
DataBean dataBean = new DataBean(GuiPlotMode.SCATTER3D);
DatasetWithAxisInformation axisData = new DatasetWithAxisInformation();
AxisMapBean amb = new AxisMapBean();
axisData.setAxisMap(amb);
dataBean.putGuiParameter(GuiParameters.PLOTOPERATION, GuiParameters.PLOTOP_UPDATE);
axisData.setData(sizes);
try {
dataBean.addData(axisData);
} catch (DataBeanException e) {
e.printStackTrace();
}
if (xCoords != null) {
dataBean.addAxis(AxisMapBean.XAXIS, xCoords);
}
if (yCoords != null) {
dataBean.addAxis(AxisMapBean.YAXIS, yCoords);
}
if (zCoords != null) {
dataBean.addAxis(AxisMapBean.ZAXIS, zCoords);
}
setDataBean(plotName, dataBean);
}
@Override
public void surfacePlot(String plotName, IDataset xValues, IDataset yValues, IDataset data) throws Exception {
if (!isDataND(data, 2)) {
logger.error("Input dataset has incorrect rank: it has {} dimensions when it should be 2", data.getRank());
throw new Exception("Input dataset has incorrect rank: it should be 2");
}
if (xValues != null && !isDataND(xValues, 1)) {
String msg = String.format("X axis dataset has incorrect rank: it has %d dimensions when it should be 1",
xValues.getRank());
logger.error(msg);
throw new Exception(msg);
}
if (yValues != null && !isDataND(yValues, 1)) {
String msg = String.format("Y axis dataset has incorrect rank: it has %d dimensions when it should be 1",
yValues.getRank());
logger.error(msg);
throw new Exception(msg);
}
AxisMapBean amb = new AxisMapBean();
DatasetWithAxisInformation axisData = new DatasetWithAxisInformation();
axisData.setAxisMap(amb);
axisData.setData(data);
DataBean dataBean = new DataBean(GuiPlotMode.SURF2D);
try {
dataBean.addData(axisData);
} catch (DataBeanException e) {
e.printStackTrace();
}
if (xValues != null) {
dataBean.addAxis(AxisMapBean.XAXIS, xValues);
}
if (yValues != null) {
dataBean.addAxis(AxisMapBean.YAXIS, yValues);
}
setDataBean(plotName, dataBean);
}
@Override
public void stackPlot(String plotName, IDataset[] xValues, IDataset[] yValues, final IDataset zValues) throws Exception {
lstackPlot(plotName, validateAllXValues(xValues, yValues), yValues, zValues, GuiParameters.PLOTOP_NONE);
}
@Override
public void addStackPlot(String plotName, IDataset[] xValues, IDataset[] yValues, final IDataset zValues) throws Exception {
lstackPlot(plotName, validateAllXValues(xValues, yValues), yValues, zValues, GuiParameters.PLOTOP_ADD);
}
@Override
public void updateStackPlot(String plotName, IDataset[] xValues, IDataset[] yValues, final IDataset zValues) throws Exception {
lstackPlot(plotName, validateAllXValues(xValues, yValues), yValues, zValues, GuiParameters.PLOTOP_UPDATE);
}
/**
* Plot a stack in 3D of 1D line plots to named view
* @param plotName
* @param xValues
* @param yValues
* @param zValues
* @param plotOperation one of GuiParameters.PLOTOP_*
* @param updateMode if true, keep zoom settings
* @throws Exception
*/
private void lstackPlot(String plotName, IDataset[] xValues, IDataset[] yValues, IDataset zValues, String plotOperation) throws Exception {
for (IDataset x : xValues) {
if (!isDataND(x, 1)) {
logger.error("Input x dataset has incorrect rank: it has {} dimensions when it should be 1",
x.getRank());
throw new Exception("Input x dataset has incorrect rank: it should be 1");
}
}
for (IDataset y : yValues) {
if (!isDataND(y, 1)) {
logger.error("Input y dataset has incorrect rank: it has {} dimensions when it should be 1",
y.getRank());
throw new Exception("Input y dataset has incorrect rank: it should be 1");
}
}
DataBean dataBean = new DataBean(GuiPlotMode.ONED_THREED);
dataBean.putGuiParameter(GuiParameters.PLOTOPERATION, plotOperation);
if (xValues.length == 1) {
dataBean.addAxis(AxisMapBean.XAXIS, xValues[0]);
for (int i = 0; i < yValues.length; i++) {
// now add it to the plot data
try {
dataBean.addData(DatasetWithAxisInformation.createAxisDataSet(yValues[i]));
} catch (DataBeanException e) {
e.printStackTrace();
}
}
} else {
if (xValues.length != yValues.length)
throw new Exception("# xValues does not match # yValues");
for (int i = 0; i < xValues.length; i++) {
String axisStr = AxisMapBean.XAXIS + i;
dataBean.addAxis(axisStr, xValues[i]);
// now add it to the plot data
try {
dataBean.addData(DatasetWithAxisInformation.createAxisDataSet(yValues[i], axisStr));
} catch (DataBeanException e) {
e.printStackTrace();
}
}
}
if (zValues != null) {
if (!isDataND(zValues, 1)) {
logger.error("Input z dataset has incorrect rank: it has {} dimensions when it should be 1",
zValues.getRank());
throw new Exception("Input z dataset has incorrect rank: it should be 1");
}
dataBean.addAxis(AxisMapBean.ZAXIS, zValues);
}
setDataBean(plotName, dataBean);
}
@Override
public int scanForImages(String viewName, String pathname, int order, String nameregex, String[] suffices,
int gridColumns, boolean rowMajor, int maxFiles, int jumpBetween) throws Exception {
File file = new File(pathname);
PlotService plotServer = getPlotService();
int filesPushed = 0;
if (plotServer != null) {
int numFiles = 0;
if (file.isDirectory()) {
// GuiBean guiBean = getGuiStateForPlotMode(viewName, GuiPlotMode.MULTI2D);
GuiBean guiBean = new GuiBean();
guiBean.put(GuiParameters.PLOTMODE, GuiPlotMode.IMGEXPL);
File[] files = file.listFiles();
if (suffices == null)
suffices = LISTOFSUFFIX;
List<String> imageFiles = filterImages(files, nameregex, suffices);
int nImages = imageFiles.size();
int gridRows = (int) (gridColumns > 0 ? Math.ceil(nImages / (double) gridColumns) : Math.ceil(Math
.sqrt(nImages)));
if (gridRows == 0)
gridRows = 1;
int gridCols = gridColumns > 0 ? gridColumns : gridRows;
guiBean.put(GuiParameters.IMAGEGRIDSIZE, new Integer[] { gridRows, gridCols });
plotServer.updateGui(viewName, guiBean);
guiBean.remove(GuiParameters.IMAGEGRIDSIZE);
if (nImages == 0)
return filesPushed;
switch (order) {
case IMAGEORDERALPHANUMERICAL:
Collections.sort(imageFiles); // could make faster by removing dirname first
break;
case IMAGEORDERCHRONOLOGICAL:
chronoSort(imageFiles);
break;
case IMAGEORDERNONE:
break;
}
Iterator<String> iter = imageFiles.iterator();
if (rowMajor) {
while (iter.hasNext() && numFiles < maxFiles) {
String filename = iter.next();
if (numFiles % jumpBetween == 0) {
guiBean.put(GuiParameters.FILENAME, filename);
plotServer.updateGui(viewName, guiBean);
filesPushed++;
}
numFiles++;
}
} else {
int x = 0;
int y = 0;
while (iter.hasNext() && numFiles < maxFiles) {
String filename = iter.next();
if (numFiles % jumpBetween == 0) {
guiBean.put(GuiParameters.IMAGEGRIDXPOS, Integer.valueOf(x));
guiBean.put(GuiParameters.IMAGEGRIDYPOS, Integer.valueOf(y));
guiBean.put(GuiParameters.FILENAME, filename);
plotServer.updateGui(viewName, guiBean);
filesPushed++;
y++;
}
numFiles++;
if (y == gridRows) {
y = 0;
x++;
}
}
}
} else {
logger.warn("Given path was not a directory");
}
}
return filesPushed;
}
private List<String> filterImages(File[] files, String regex, String[] suffices) {
List<String> listOfImages = new ArrayList<String>();
if (suffices == null && regex == null) {
for (int i = 0; i < files.length; i++) {
listOfImages.add(files[i].getAbsolutePath());
}
} else {
StringBuilder fullregex = new StringBuilder(regex == null ? ".*" : regex);
if (suffices != null && suffices.length > 0) {
if (suffices.length == 1) {
fullregex.append(suffices[0]);
} else {
fullregex.append('(');
for (String s : suffices) {
fullregex.append(s);
fullregex.append('|');
}
final int end = fullregex.length();
fullregex.replace(end - 1, end, ")");
}
Pattern p = Pattern.compile(fullregex.toString());
for (File f : files) {
Matcher m = p.matcher(f.getName().toLowerCase());
if (m.matches()) {
listOfImages.add(f.getAbsolutePath());
continue;
}
}
}
}
return listOfImages;
}
private void chronoSort(List<String> imageFiles) {
TreeMap<Long, String> imap = new TreeMap<Long, String>();
for (String s : imageFiles) {
imap.put(new File(s).lastModified(), s);
}
imageFiles.clear();
imageFiles.addAll(imap.values());
}
@Override
public void volumePlot(String viewName, String rawvolume, int headerSize, int voxelType, int xdim, int ydim,
int zdim) throws Exception {
GuiBean guiBean = new GuiBean();
guiBean.put(GuiParameters.PLOTMODE, GuiPlotMode.VOLUME);
guiBean.put(GuiParameters.FILENAME, rawvolume);
guiBean.put(GuiParameters.VOLUMEHEADERSIZE, Integer.valueOf(headerSize));
guiBean.put(GuiParameters.VOLUMEVOXELTYPE, Integer.valueOf(voxelType));
guiBean.put(GuiParameters.VOLUMEXDIM, Integer.valueOf(xdim));
guiBean.put(GuiParameters.VOLUMEYDIM, Integer.valueOf(ydim));
guiBean.put(GuiParameters.VOLUMEZDIM, Integer.valueOf(zdim));
setGuiBean(viewName, guiBean);
}
@Override
public void volumePlot(String viewName, IDataset volume) throws Exception {
if (!isDataND(volume, 3)) {
logger.error("Input dataset has incorrect rank: it has {} dimensions when it should be 3", volume.getRank());
throw new Exception("Input dataset has incorrect rank: it should be 3");
}
DataHolder tHolder = new DataHolder();
try {
Dataset v = DatasetUtils.convertToDataset(volume);
if (v instanceof CompoundDataset) {
CompoundDataset cd = (CompoundDataset) v;
v = cd.getElements(0);
}
switch (v.getDType()) {
case Dataset.BOOL:
v = v.cast(Dataset.FLOAT32);
break;
case Dataset.FLOAT64:
v = v.cast(Dataset.FLOAT32);
break;
case Dataset.INT32:
v = v.cast(Dataset.FLOAT32);
break;
case Dataset.INT64:
v = v.cast(Dataset.FLOAT32);
break;
}
//FIXME This is a horrible hack to get round some bugs in GigaCube. Should be fixed in the GigaCube code as soon as possible
int[] shape = new int[] {v.getShape()[2], v.getShape()[1], v.getShape()[0]};
v.setShape(shape);
String filename = saveTempFile(tHolder, v);
volumePlot(viewName, filename);
} catch (ScanFileHolderException e) {
throw new Exception("Failed to save to temporary file");
}
}
@Override
public void volumePlot(String viewName, String dsrvolume) throws Exception {
GuiBean guiBean = new GuiBean();
guiBean.put(GuiParameters.PLOTMODE, GuiPlotMode.VOLUME);
guiBean.put(GuiParameters.FILENAME, dsrvolume);
setGuiBean(viewName, guiBean);
}
@Override
public void clearPlot(String viewName) throws Exception {
PlotService plotServer = getPlotService();
if (plotServer != null) {
GuiBean guiBean = new GuiBean();
guiBean.put(GuiParameters.PLOTMODE, GuiPlotMode.EMPTY);
plotServer.updateGui(viewName, guiBean);
plotServer.setData(viewName, null); // remove data too
}
}
@Override
public void exportPlot(String viewName, String fileFormat, String saveFullPath) throws Exception {
GuiBean guiBean = new GuiBean();
guiBean.put(GuiParameters.PLOTMODE, GuiPlotMode.EXPORT);
guiBean.put(GuiParameters.FILEFORMAT, fileFormat);
guiBean.put(GuiParameters.SAVEPATH, saveFullPath);
setGuiBean(viewName, guiBean);
}
@Override
public void resetAxes(String viewName) throws Exception {
GuiBean guiBean = new GuiBean();
guiBean.put(GuiParameters.PLOTMODE, GuiPlotMode.RESETAXES);
setGuiBean(viewName, guiBean);
}
@Override
public void setupNewImageGrid(String viewName, int gridRows, int gridColumns) throws Exception {
// GuiBean guiBean = new GuiBean();
// guiBean.put(GuiParameters.PLOTMODE, GuiPlotMode.IMGEXPL);
// guiBean.put(GuiParameters.IMAGEGRIDSIZE, new Integer[] { gridRows, gridColumns });
// setGuiBean(viewName, guiBean);
DataBean databean = new DataBean();
databean.putGuiParameter(GuiParameters.PLOTMODE, GuiPlotMode.IMGEXPL);
databean.putGuiParameter(GuiParameters.IMAGEGRIDSIZE, new Integer[] { gridRows, gridColumns });
setDataBean(viewName, databean);
}
@Override
public void plotImageToGrid(String viewName, IDataset[] datasets, boolean store) throws Exception {
for (int i = 0; i < datasets.length; i++) {
setDataBean(viewName, "", datasets[i], -1, -1);
}
}
@Override
public void plotImageToGrid(String viewName, String filename, int gridX, int gridY) throws Exception {
setDataBean(viewName, filename, null, gridX, gridY);
}
@Override
public void plotImageToGrid(String viewName, IDataset dataset, int gridX, int gridY, boolean store)
throws Exception {
setDataBean(viewName, "", dataset, gridX, gridY);
}
private void setDataBean(String viewName, String filename, IDataset dataset, int gridX, int gridY) throws Exception {
DataBean databean = new DataBean();
List<DatasetWithAxisInformation> data = new ArrayList<DatasetWithAxisInformation>();
DatasetWithAxisInformation d = new DatasetWithAxisInformation();
if (dataset != null) {
d.setData((IDataset) dataset);
data.add(d);
databean.setData(data);
databean.putGuiParameter(GuiParameters.FILENAME, dataset.getName());
} else {
databean.putGuiParameter(GuiParameters.FILENAME, filename);
}
databean.putGuiParameter(GuiParameters.PLOTMODE, GuiPlotMode.IMGEXPL);
if (gridX >= 0 && gridY >= 0) {
databean.putGuiParameter(GuiParameters.IMAGEGRIDXPOS, Integer.valueOf(gridX));
databean.putGuiParameter(GuiParameters.IMAGEGRIDYPOS, Integer.valueOf(gridY));
}
setDataBean(viewName, databean);
}
protected String saveTempFile(DataHolder tHolder, IDataset dataset) throws ScanFileHolderException {
try {
java.io.File tmpFile = File.createTempFile("image-explorer-store-", ".raw");
String dirName = System.getProperty("java.io.tmpdir");
java.io.File directory = new java.io.File(dirName);
if (!directory.exists()) {
directory.mkdir();
}
String rawFilename = directory.getAbsolutePath() + System.getProperty("file.separator") + tmpFile.getName();
tHolder.setDataset("Data", dataset);
new RawBinarySaver(rawFilename).saveFile(tHolder);
return rawFilename;
} catch (IOException e) {
throw new ScanFileHolderException("Couldn't save file", e);
}
}
@Override
public void viewTree(String viewer, Tree tree) throws Exception {
logger.info("Tree sent to {}", viewer);
DataBean db = new DataBean(null);
db.addTree(tree);
setDataBean(viewer, db);
}
/**
* Simple method which forwards the data to the plot server
*
* @param plotName
* The name of the plot
* @param dataBean
* The data to be passed to the plot server (can be null)
* @param guiBean
* The gui data which is included for the Plot legend
* @throws Exception
*/
private void sendBeansToServer(String plotName, DataBean dataBean, GuiBean guiBean) throws Exception {
PlotService plotServer = getPlotService();
if (plotServer == null)
return;
if (guiBean != null) {
guiBean.remove(GuiParameters.PLOTID); // remove any previous ID now it is being pushed to all the clients
plotServer.updateGui(plotName, guiBean);
}
if (dataBean != null) {
plotServer.setData(plotName, dataBean);
}
}
@Override
public void setGuiBean(String plotName, GuiBean bean) throws Exception {
sendBeansToServer(plotName, null, bean);
}
@Override
public GuiBean getGuiBean(String plotName) throws Exception {
PlotService plotService = getPlotService();
return plotService == null ? null : plotService.getGuiState(plotName);
}
@Override
public void setDataBean(String plotName, DataBean bean) throws Exception {
sendBeansToServer(plotName, bean, null);
}
@Override
public DataBean getDataBean(String plotName) throws Exception {
PlotService plotService = getPlotService();
return plotService == null ? null : plotService.getData(plotName);
}
@Override
public GuiBean getGuiStateForPlotMode(String plotName, GuiPlotMode plotMode) {
GuiBean bean;
try {
bean = getGuiBean(plotName);
if (bean != null) {
if (bean.containsKey(GuiParameters.PLOTMODE)) {
if (!bean.get(GuiParameters.PLOTMODE).equals(plotMode)) {
bean = null;
} else {
bean.remove(GuiParameters.PLOTID);
}
}
}
} catch (Exception e) {
bean = null;
}
if (bean == null) {
bean = new GuiBean();
bean.put(GuiParameters.PLOTMODE, plotMode);
}
return bean;
}
@Override
public String[] getGuiNames() throws Exception {
return getPlotService().getGuiNames();
}
@Override
public void createAxis(String plotName, final String title, final int side) throws Exception {
GuiBean guiBean = new GuiBean();
guiBean.put(GuiParameters.AXIS_OPERATION, new AxisOperation(AxisOperation.CREATE, title, side));
setGuiBean(plotName, guiBean);
}
@Override
public void renameActiveXAxis(String plotName, String xAxisTitle) throws Exception {
GuiBean guiBean = new GuiBean();
guiBean.put(GuiParameters.AXIS_OPERATION, new AxisOperation(AxisOperation.RENAMEX, xAxisTitle));
setGuiBean(plotName, guiBean);
}
@Override
public void renameActiveYAxis(String plotName, String yAxisTitle) throws Exception {
GuiBean guiBean = new GuiBean();
guiBean.put(GuiParameters.AXIS_OPERATION, new AxisOperation(AxisOperation.RENAMEY, yAxisTitle));
setGuiBean(plotName, guiBean);
}
}