Java tutorial
/* * 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.rcp.inspector; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.io.File; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import org.apache.commons.lang.ArrayUtils; import org.dawnsci.plotting.jreality.impl.DataSet3DPlot2DMulti; import org.eclipse.dawnsci.analysis.api.dataset.IDataset; import org.eclipse.dawnsci.analysis.api.dataset.ILazyDataset; import org.eclipse.dawnsci.analysis.api.dataset.Slice; import org.eclipse.dawnsci.analysis.api.metadata.IMetadata; import org.eclipse.dawnsci.analysis.api.monitor.IMonitor; import org.eclipse.dawnsci.analysis.dataset.impl.Dataset; import org.eclipse.dawnsci.analysis.dataset.impl.DatasetUtils; import org.eclipse.dawnsci.analysis.dataset.impl.IntegerDataset; import org.eclipse.dawnsci.analysis.dataset.impl.PositionIterator; import org.eclipse.dawnsci.plotting.api.IPlottingSystem; import org.eclipse.dawnsci.plotting.api.PlottingFactory; import org.eclipse.dawnsci.plotting.api.tool.IToolPage.ToolPageRole; import org.eclipse.dawnsci.plotting.api.tool.IToolPageSystem; import org.eclipse.swt.SWT; import org.eclipse.swt.custom.ScrolledComposite; import org.eclipse.swt.events.SelectionAdapter; import org.eclipse.swt.events.SelectionEvent; import org.eclipse.swt.events.SelectionListener; import org.eclipse.swt.layout.GridData; import org.eclipse.swt.layout.GridLayout; import org.eclipse.swt.widgets.Button; import org.eclipse.swt.widgets.Combo; import org.eclipse.swt.widgets.Composite; import org.eclipse.swt.widgets.Control; import org.eclipse.swt.widgets.Label; import org.eclipse.swt.widgets.Link; import org.eclipse.ui.IWorkbenchPage; import org.eclipse.ui.IWorkbenchPartSite; import org.eclipse.ui.PartInitException; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import uk.ac.diamond.scisoft.analysis.SDAPlotter; import uk.ac.diamond.scisoft.analysis.axis.AxisChoice; import uk.ac.diamond.scisoft.analysis.rcp.editors.CompareFilesEditor; import uk.ac.diamond.scisoft.analysis.rcp.explorers.AbstractExplorer; import uk.ac.diamond.scisoft.analysis.rcp.inspector.DatasetSelection.InspectorType; import uk.ac.diamond.scisoft.analysis.rcp.views.DatasetTableView; import uk.ac.diamond.scisoft.analysis.rcp.views.ImageExplorerView; /** * All inspection tabs obey this interface */ interface InspectionTab { /** * @return string to be used for title of tab */ public String getTabTitle(); /** * Create the composite for tab * @param parent * @return composite */ public Composite createTabComposite(Composite parent); /** * Set the dataset and axis selection for tab * @param data * @param datasetAxisList * @param plotAxislist */ public void setParameters(ILazyDataset data, List<AxisSelection> datasetAxisList, List<PlotAxisProperty> plotAxislist); /** * Show slice of dataset using tab configuration * @param monitor * @param slices */ public void pushToView(IMonitor monitor, List<SliceProperty> slices); /** * @return true if tab can plot constant in place of dataset */ public boolean canPlotConstant(); /** * Check whether data has sufficient rank for tab * @param data * @return true if data is compatible with tab */ public boolean checkCompatible(ILazyDataset data); /** * Draw tab */ public void drawTab(); /** * Clear axes */ public void resetAxes(); /** * @return number of axes (used in inspection) */ public int getNumAxes(); /** * @return inspector type of tab */ public InspectorType getType(); /** * @return boolean array of which dimensions are used as axes */ public boolean[] getUsedDims(); /** * Stop inspection process in tab (for long running processes) */ public void stopInspection(); /** * @return control */ public Control getControl(); } /** * Abstract base class */ abstract class ATab implements InspectionTab { protected static final Logger logger = LoggerFactory.getLogger(ATab.class); protected static final String PLOTNAME = "Dataset Plot"; protected String text; protected String[] axes; protected List<Label> axisLabels; protected List<Combo> combos; protected List<AxisSelection> daxes = null; protected List<PlotAxisProperty> paxes = null; protected Composite composite; protected InspectorType itype; protected IWorkbenchPartSite site; protected ILazyDataset dataset; protected int comboOffset = 0; public ATab(IWorkbenchPartSite partSite, InspectorType type, String title, String[] axisNames) { site = partSite; itype = type; text = title; axes = axisNames; } @Override public void setParameters(ILazyDataset data, List<AxisSelection> datasetAxisList, List<PlotAxisProperty> plotAxisList) { dataset = data; daxes = datasetAxisList; paxes = plotAxisList; } @Override final public String getTabTitle() { return text; } @Override final public InspectorType getType() { return itype; } @Override public boolean checkCompatible(ILazyDataset data) { boolean isCompatible = data.getRank() >= axes.length; if (composite != null) composite.setEnabled(isCompatible); return isCompatible; } @Override final public int getNumAxes() { if (axes == null) return 0; return axes.length; } @Override public boolean canPlotConstant() { return false; } @Override public void resetAxes() { try { SDAPlotter.resetAxes(PLOTNAME); } catch (Exception e) { logger.error("Could not clear plot", e); } } @Override public Control getControl() { return composite; } } /** * Straightforward plotting tabs */ class PlotTab extends ATab { private static final String VOLVIEWNAME = "Remote Volume Viewer"; private String explorerName; // this is the current limit on the number of lines that stack can handle well private static final int STACKPLOTLIMIT = 100; private static final int MULTIIMAGESLIMIT = DataSet3DPlot2DMulti.MAX_IMAGES; private PropertyChangeListener axesListener = null; private ImageExplorerView explorer = null; protected boolean runLongJob = false; private boolean plotStackIn3D = false; public PlotTab(IWorkbenchPartSite partSite, InspectorType type, String title, String[] axisNames) { super(partSite, type, title, axisNames); } @Override public Composite createTabComposite(Composite parent) { ScrolledComposite sComposite = new ScrolledComposite(parent, SWT.H_SCROLL | SWT.V_SCROLL); Composite holder = new Composite(sComposite, SWT.NONE); holder.setLayout(new GridLayout(2, false)); axisLabels = new ArrayList<Label>(); combos = new ArrayList<Combo>(); SelectionAdapter listener = new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { Combo c = (Combo) e.widget; int i = combos.indexOf(c); if (i >= 0 && paxes != null) { PlotAxisProperty p = paxes.get(i); String item = c.getItem(c.getSelectionIndex()); if (item.equals(p.getName())) return; p.setName(item, false); repopulateCombos(null, null); } } }; createCombos(holder, listener); if (daxes != null) populateCombos(); if (itype == InspectorType.LINESTACK) { final Button b = new Button(holder, SWT.CHECK); b.setText("In 3D"); b.setToolTipText("Check to plot stack of lines in 3D"); b.setSelection(plotStackIn3D); b.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { plotStackIn3D = b.getSelection(); if (paxes != null) { // signal a replot without a slice reset PlotAxisProperty p = paxes.get(0); p.fire(new PropertyChangeEvent(p, PlotAxisProperty.plotUpdate, p.getName(), p.getName())); } } }); } else if (itype == InspectorType.SURFACE) { final IPlottingSystem plottingSystem = PlottingFactory.getPlottingSystem(PLOTNAME); final Link openWindowing = new Link(holder, SWT.WRAP); openWindowing.setText("Open the <a>Slice Window</a>"); openWindowing.setLayoutData(new GridData(SWT.LEFT, SWT.CENTER, true, false, 2, 0)); openWindowing.addSelectionListener(new SelectionAdapter() { @Override public void widgetSelected(SelectionEvent e) { if (plottingSystem != null) { try { final IToolPageSystem system = (IToolPageSystem) plottingSystem .getAdapter(IToolPageSystem.class); system.setToolVisible("org.dawb.workbench.plotting.tools.windowTool", ToolPageRole.ROLE_3D, "org.dawb.workbench.plotting.views.toolPageView.3D"); } catch (Exception e1) { logger.error("Cannot open window tool!", e1); } } } }); } holder.setLayoutData(new GridData(SWT.FILL, SWT.CENTER, true, false)); sComposite.setContent(holder); holder.setSize(holder.computeSize(SWT.DEFAULT, SWT.DEFAULT)); composite = sComposite; return composite; } protected void createCombos(Composite cHolder, SelectionListener listener) { for (int i = 0; i < axes.length; i++) { // create combo box for each axis Label l = new Label(cHolder, SWT.NONE); l.setText(axes[i]); axisLabels.add(l); Combo c = new Combo(cHolder, SWT.READ_ONLY); c.add(" "); c.addSelectionListener(listener); GridData gd = new GridData(SWT.FILL, SWT.BEGINNING, true, false); c.setLayoutData(gd); combos.add(c); } cHolder.layout(); } @Override public boolean checkCompatible(ILazyDataset data) { if (itype == InspectorType.IMAGEXP) { boolean isCompatible = data.getRank() >= (axes.length - 1); if (composite != null) composite.setEnabled(isCompatible); return isCompatible; } return super.checkCompatible(data); } @Override public void drawTab() { repopulateCombos(null, null); } @Override public synchronized void stopInspection() { runLongJob = false; } private synchronized void setInspectionRunning() { runLongJob = true; } private synchronized boolean canContinueInspection() { return runLongJob; } @Override public void setParameters(ILazyDataset data, List<AxisSelection> datasetAxisList, List<PlotAxisProperty> plotAxisList) { if (axesListener != null && daxes != null) { for (AxisSelection a : daxes) { a.removePropertyChangeListener(axesListener); } } super.setParameters(data, datasetAxisList, plotAxisList); if (daxes != null) { if (axesListener == null) { axesListener = new PropertyChangeListener() { @Override public void propertyChange(PropertyChangeEvent evt) { repopulateCombos(evt.getOldValue().toString(), evt.getNewValue().toString()); } }; } for (AxisSelection a : daxes) { a.addPropertyChangeListener(axesListener); } } if (combos != null) populateCombos(); } /** * @return list of axis datasets */ protected List<AxisChoice> getChosenAxes() { List<String> names = getChosenAxisNames(); List<AxisChoice> list = new ArrayList<AxisChoice>(); for (AxisSelection s : daxes) { AxisChoice a = null; String sname = s.getSelectedName(); if (names.indexOf(sname) != -1) { a = s.getAxis(sname); names.remove(sname); } if (a != null) { list.add(a); } else { // logger.warn("No axis of names {} found in selection {}", names, s); list.add(s.getSelectedAxis()); } } return list; } final protected LinkedList<String> getChosenAxisNames() { // get all chosen axis names from combo boxes LinkedList<String> pAxes = new LinkedList<String>(); if (paxes != null) { for (PlotAxisProperty p : paxes) { if (!p.isInSet()) continue; String n = p.getName(); if (n != null) { pAxes.add(n); } else { logger.error("No axis selected in {}", p); } } } return pAxes; } final protected LinkedList<String> getSelectedAxisNames() { // get all selected axes LinkedList<String> sAxes = new LinkedList<String>(); if (daxes != null) { for (AxisSelection s : daxes) { String n = s.getSelectedName(); if (n != null) { sAxes.add(n); } else { logger.error("No axis selected in {}", s); } } } return sAxes; } // get selected axes to add into combos final protected HashMap<Integer, String> getSelectedComboAxisNames() { HashMap<Integer, String> sAxes = new HashMap<Integer, String>(); if (daxes != null) { for (int i = 0, imax = daxes.size(); i < imax; i++) { AxisSelection s = daxes.get(i); String n = s.getSelectedName(); if (n != null) { sAxes.put(i, n); } else { logger.warn("No axis selection available in {}", s); } } } return sAxes; } @Override public boolean[] getUsedDims() { List<String> sAxes = getSelectedAxisNames(); List<String> cAxes = getChosenAxisNames(); HashSet<String> chosenAxes = new HashSet<String>(); int cSize = combos.size() - comboOffset; for (int i = 0; i < cSize; i++) { PlotAxisProperty p = paxes.get(i + comboOffset); chosenAxes.add(p.getName()); } if (chosenAxes.size() != cAxes.size()) { logger.debug("Chosen sets are unequal in size!"); } for (String s : cAxes) { if (!chosenAxes.contains(s)) { logger.debug("Chosen set does not contain " + s); } } boolean[] used = new boolean[sAxes.size()]; for (int i = 0, imax = sAxes.size(); i < imax; i++) { ILazyDataset selectedAxis = daxes.get(i).getSelectedAxis().getValues(); if (selectedAxis == null) { continue; } used[i] = chosenAxes.contains(sAxes.get(i)); } return used; } final public int[] getOrder(int rank) { if (rank == 1) return new int[] { 0 }; LinkedList<Integer> orders = new LinkedList<Integer>(); for (int i = 0; i < rank; i++) orders.add(i); int[] cOrder = new int[rank]; int i = 0; for (PlotAxisProperty p : paxes) { if (!p.isInSet()) continue; int d = p.getDimension(); cOrder[i++] = d; orders.remove((Integer) d); } for (; i < rank; i++) { int d = orders.removeFirst(); cOrder[i] = d; } return cOrder; } private static final String IMAGE_EXP_AXIS_LABEL = "images"; protected void populateCombos() { int cSize = combos.size() - comboOffset; HashMap<Integer, String> sAxes = getSelectedComboAxisNames(); for (int i = 0; i < cSize; i++) { Combo c = combos.get(i + comboOffset); c.removeAll(); PlotAxisProperty p = paxes.get(i + comboOffset); p.clear(); Label l = axisLabels.get(i + comboOffset); if (sAxes.size() == 0) { p.setInSet(false); c.setEnabled(false); c.setVisible(false); l.setVisible(false); if (itype == InspectorType.IMAGEXP) { // hack to change labels l = axisLabels.get(i + comboOffset - 1); l.setText(IMAGE_EXP_AXIS_LABEL); l.getParent().layout(); } break; } c.setEnabled(true); c.setVisible(true); l.setVisible(true); if (itype == InspectorType.IMAGEXP && l.getText().equals(IMAGE_EXP_AXIS_LABEL)) { l.setText(axes[i + comboOffset]); // reset label l.getParent().layout(); } ArrayList<Integer> keyList = new ArrayList<Integer>(sAxes.keySet()); Collections.sort(keyList); Integer lastKey = keyList.get(keyList.size() - 1); String a = sAxes.get(lastKey); // reverse order if (axes.length == 1) { // for 1D plots and 1D dataset table, remove single point axes int[] shape = dataset.getShape(); while (shape.length > lastKey && shape[lastKey] == 1) { lastKey--; } a = sAxes.get(lastKey); // reverse order for (int j : keyList) { String n = sAxes.get(j); p.put(j, n); if (shape.length > j && shape[j] != 1) c.add(n); } } else { for (int j : keyList) { String n = sAxes.get(j); p.put(j, n); c.add(n); } } c.setText(a); sAxes.remove(lastKey); p.setName(a, false); p.setInSet(true); } } protected void repopulateCombos(String oldName, String newName) { if (combos == null) return; // cascade through plot axes strings and indices // reduce choice each time HashMap<Integer, String> sAxes = getSelectedComboAxisNames(); if (sAxes.size() == 0) return; int cSize = combos.size() - comboOffset; int dmax = daxes.size(); String a = null; if (oldName != null && newName != null) { // only one dataset axis has changed LinkedList<String> oAxes = paxes.get(comboOffset).getNames(); // old axes if (dmax != oAxes.size()) { logger.error("First axis combo has less choice than rank of dataset"); return; } // find changed dimension LinkedList<String> cAxes = getChosenAxisNames(); // old choices Map<String, Integer> axesMap = new LinkedHashMap<String, Integer>(); Map<String, Integer> oldMap = paxes.get(comboOffset).getValue().getMap(); for (String n : oldMap.keySet()) { axesMap.put(n.equals(oldName) ? newName : n, oldMap.get(n)); } PlotAxisProperty p = null; for (int i = 0; i < cSize; i++) { Combo c = combos.get(i + comboOffset); c.removeAll(); p = paxes.get(i + comboOffset); p.clear(); String curAxis = cAxes.get(i); a = oldName.equals(curAxis) ? newName : curAxis; if (axes.length == 1) { // for 1D plots and 1D dataset table int[] shape = dataset.getShape(); for (String n : axesMap.keySet()) { Integer j = axesMap.get(n); p.put(j, n); if (shape[j] != 1) c.add(n); } } else { for (String n : axesMap.keySet()) { Integer j = axesMap.get(n); p.put(j, n); c.add(n); } } c.setText(a); axesMap.remove(a); p.setName(a, false); p.setInSet(true); } // do not need to notify plot axes listeners if (a != null && p != null) { if (p.isInSet()) { p.setName(a); } } return; } PlotAxisProperty p = paxes.get(comboOffset); Map<String, Integer> axesMap = new LinkedHashMap<String, Integer>(p.getValue().getMap()); a = p.getName(); axesMap.remove(a); for (int i = 1; i < cSize; i++) { Combo c = combos.get(i + comboOffset); p = paxes.get(i + comboOffset); a = p.getName(); c.removeAll(); p.clear(); if (a == null) { break; } if (!axesMap.containsKey(a)) { a = axesMap.keySet().iterator().next(); // attempt to get a valid name } if (axes.length == 1) { // for 1D plots and 1D dataset table int[] shape = dataset.getShape(); for (String n : axesMap.keySet()) { Integer j = axesMap.get(n); p.put(j, n); if (shape[j] != 1) c.add(n); } } else { for (String n : axesMap.keySet()) { Integer j = axesMap.get(n); p.put(j, n); c.add(n); } } c.setText(a); axesMap.remove(a); p.setName(a, false); } if (a != null) { if (p.isInSet()) { p.setName(a); } } } protected List<Dataset> sliceAxes(List<AxisChoice> axes, Slice[] slices, boolean[] average, int[] order) { List<Dataset> slicedAxes = new ArrayList<Dataset>(); boolean[] used = getUsedDims(); for (int o : order) { if (used[o]) { AxisChoice c = axes.get(o); int[] imap = c.getIndexMapping(); // We need to reorder multidimensional axis values to match reorder data int[] reorderAxesDims = new int[imap.length]; for (int i = 0, j = 0; i < order.length && j < imap.length; i++) { int idx = ArrayUtils.indexOf(imap, order[i]); if (idx != ArrayUtils.INDEX_NOT_FOUND) reorderAxesDims[j++] = idx; } ILazyDataset axesData = c.getValues(); int[] shape = axesData.getShape(); Slice[] s = new Slice[imap.length]; for (int i = 0; i < s.length; i++) { Slice ts = slices[imap[i]]; if (ts.getLength() <= shape[i]) { s[i] = ts.clone(); if (average[imap[i]]) { Integer start = s[i].getStart(); start = start == null ? 0 : start; s[i].setStop(start + 1); } } } Dataset slicedAxis = DatasetUtils.convertToDataset(axesData.getSlice(s)); Dataset reorderdAxesData = slicedAxis.getTransposedView(reorderAxesDims); // reorderdAxesData.setName(axesData.getName()); reorderdAxesData.setName(c.getLongName()); slicedAxes.add(reorderdAxesData.squeeze()); } } return slicedAxes; } protected Dataset sliceData(IMonitor monitor, Slice[] slices) { Dataset slicedData = null; try { if (dataset instanceof IDataset) { slicedData = DatasetUtils.convertToDataset(dataset.getSliceView(slices)); } else { slicedData = DatasetUtils.convertToDataset(dataset.getSlice(monitor, slices)); } } catch (Exception e) { logger.error("Problem getting slice of data: {}", e); logger.error("Tried to get slices: {}", Arrays.toString(slices)); } return slicedData; } protected Dataset sliceData(IMonitor monitor, int[] start, int[] stop, int[] step) { Dataset slicedData = null; try { if (dataset instanceof IDataset) { slicedData = DatasetUtils.convertToDataset(dataset.getSliceView(start, stop, step)); } else { slicedData = DatasetUtils.convertToDataset(dataset.getSlice(monitor, start, stop, step)); } } catch (Exception e) { logger.error("Problem getting slice of data: {}", e); logger.error("Tried to get slice: start={}, stop={}, step={}", new Object[] { Arrays.toString(start), Arrays.toString(stop), Arrays.toString(step) }); } return slicedData; } protected Dataset slicedAndReorderData(IMonitor monitor, Slice[] slices, boolean[] average, int[] order, IMetadata meta) { Dataset reorderedData = null; Dataset slicedData = null; if (ArrayUtils.contains(average, true)) { Dataset averagedData = null; Dataset averagedError = null; List<Integer> axs = new ArrayList<Integer>(); int[] slicesShape = new int[slices.length]; int resDim = 0; for (int idx = 0; idx < slices.length; idx++) { slicesShape[idx] = slices[idx].getNumSteps(); if (!average[idx]) { axs.add(idx); if (slicesShape[idx] > 1) resDim++; } } // For 1D data preload last averaged dimension into memory to reduce number of data slicing calls int meanAxis = -1; if (resDim == 1) { meanAxis = slicesShape.length - 1; while (meanAxis >= 0 && !average[meanAxis]) meanAxis--; if (meanAxis != -1) axs.add(meanAxis); } PositionIterator pitr = new PositionIterator(slicesShape, ArrayUtils.toPrimitive(axs.toArray(new Integer[0]))); int sliceIdx = 0; while (pitr.hasNext()) { int[] ppos = pitr.getPos(); Slice[] tmpSlices = new Slice[slices.length]; for (int idx = 0; idx < ppos.length; idx++) { if (axs.contains(idx)) { tmpSlices[idx] = slices[idx].clone(); } else { Integer step = slices[idx].getStep(); Integer start = slices[idx].getStart() == null ? step * ppos[idx] : slices[idx].getStart() + step * ppos[idx]; Integer stop = start + step; tmpSlices[idx] = new Slice(start, stop, step); } } Dataset tmpSlice = DatasetUtils.convertToDataset(dataset.getSlice(tmpSlices)); Dataset errSlice = tmpSlice.getErrorBuffer(); // TODO remove when done internally tmpSlice.setError(null); if (meanAxis != -1) { int[] tmpShape = tmpSlice.getShape(); tmpShape[meanAxis] = 1; tmpSlice = tmpSlice.mean(meanAxis); tmpSlice.setShape(tmpShape); if (errSlice != null) { int[] errShape = errSlice.getShape(); if (errShape[meanAxis] > 1) { errShape[meanAxis] = 1; Dataset n = errSlice.count(meanAxis); errSlice = errSlice.mean(meanAxis); errSlice.idivide(n); errSlice.setShape(errShape); } } } if (averagedData != null) averagedData.iadd(tmpSlice); else averagedData = tmpSlice; if (errSlice != null) { if (averagedError != null) averagedError.iadd(errSlice); else averagedError = errSlice; } sliceIdx++; } if (averagedData == null) return null; slicedData = averagedData.idivide(sliceIdx); if (averagedError != null) { slicedData.setErrorBuffer(averagedError.idivide(sliceIdx * sliceIdx)); } } else { slicedData = sliceData(monitor, slices); } if (slicedData == null) return null; reorderedData = slicedData.getTransposedView(order); reorderedData.squeeze(); if (reorderedData.getSize() < 1) return null; // Possible fix to http://jira.diamond.ac.uk/browse/DAWNSCI-333 // ensures that file name appears in plot. final StringBuilder name = new StringBuilder(); name.append(slicedData.getName()); String path = meta == null ? null : meta.getFilePath(); if (path != null) { File file = new File(path); final String fname = file.getName(); if (fname.length() != 0 && !name.toString().contains(fname)) { name.append(" ("); name.append(fname); name.append(")"); } } reorderedData.setName(name.toString()); return reorderedData; } protected void swapFirstTwoInOrder(int[] order) { if (order.length > 1) { final int t = order[0]; order[0] = order[1]; order[1] = t; } } protected IDataset make1DAxisSlice(List<? extends IDataset> slicedAxes, int dim) { IDataset axisSlice = slicedAxes.get(dim); // 2D plots can only handle 1D axis. if (axisSlice.getRank() > 1) { int rank = axisSlice.getRank(); Slice[] sl = new Slice[rank]; for (int idx = 0; idx < rank; idx++) if (idx != dim) sl[idx] = new Slice(0, 1, 1); else sl[idx] = new Slice(); logger.warn("2D plots can only handle 1D axis. Taking first slice from {} dataset", axisSlice.getName()); IDataset d = axisSlice.getSlice(sl).squeeze(); if (d.getRank() == 0) { d.setShape(1); } return d; } if (axisSlice.getRank() == 0) axisSlice.setShape(1); return axisSlice; } /** * Check rank of dataset and correct if necessary * @param a * @param rank * @return true if something wrong */ protected boolean isRankBad(Dataset a, int rank) { if (a == null) return true; int r = a.getRank(); if (r > rank) return true; if (r == rank) return false; int[] s = Arrays.copyOf(a.getShape(), rank); for (; r < rank; r++) { s[r] = 1; } a.setShape(s); return false; } @Override public void pushToView(IMonitor monitor, List<SliceProperty> sliceProperties) { if (dataset == null) return; Slice[] slices = new Slice[sliceProperties.size()]; boolean[] average = new boolean[sliceProperties.size()]; for (int i = 0; i < slices.length; i++) { slices[i] = sliceProperties.get(i).getValue(); average[i] = sliceProperties.get(i).isAverage(); } int[] order = getOrder(daxes.size()); // FIXME: Image, surface and volume plots can't work with multidimensional axis data List<? extends IDataset> slicedAxes = sliceAxes(getChosenAxes(), slices, average, order); if (itype == InspectorType.IMAGE || itype == InspectorType.SURFACE || itype == InspectorType.IMAGEXP || itype == InspectorType.MULTIIMAGES) { // note that the DataSet plotter's 2D image/surface mode is row-major swapFirstTwoInOrder(order); } Dataset reorderedData; IMetadata meta = null; try { meta = dataset.getMetadata(); } catch (Exception e1) { logger.warn("Metadata cannot be retrieved from {}: {}", dataset.getName(), e1.getStackTrace()); } switch (itype) { case LINE: reorderedData = slicedAndReorderData(monitor, slices, average, order, meta); if (isRankBad(reorderedData, 1)) { try { SDAPlotter.clearPlot(PLOTNAME); } catch (Exception e) { logger.error("Could not clear plot", e); } return; } try { SDAPlotter.updatePlot(PLOTNAME, slicedAxes.get(0), reorderedData); } catch (Exception e) { logger.error("Could not plot 1d line"); return; } break; case LINESTACK: reorderedData = slicedAndReorderData(monitor, slices, average, order, meta); if (isRankBad(reorderedData, 2)) { try { SDAPlotter.clearPlot(PLOTNAME); } catch (Exception e) { logger.error("Could not clear plot", e); } return; } final int[] dims = reorderedData.getShape(); int lines = dims[1]; if (lines > STACKPLOTLIMIT) { logger.warn("Try plot too many lines in stack plot: reduced from {} lines to {}", lines, STACKPLOTLIMIT); int d = order[1]; SliceProperty p = sliceProperties.get(d); Slice s = p.getValue(); Integer st = s.getStart(); p.setStop((st == null ? 0 : st) + STACKPLOTLIMIT * s.getStep(), true); return; } IDataset zaxis = make1DAxisSlice(slicedAxes, 1); IDataset xaxisarray = slicedAxes.get(0); IDataset[] xaxes = new IDataset[lines]; if (xaxisarray.getRank() == 1) for (int i = 0; i < lines; i++) xaxes[i] = xaxisarray; else for (int i = 0; i < lines; i++) xaxes[i] = xaxisarray.getSlice(new int[] { 0, i }, new int[] { dims[0], i + 1 }, null) .squeeze(); Dataset[] yaxes = new Dataset[lines]; String sName = slicedAxes.get(1).getName(); boolean isDimAxis = sName.startsWith(AbstractExplorer.DIM_PREFIX) || sName.equals(CompareFilesEditor.INDEX); String dName = reorderedData.getName(); for (int i = 0; i < lines; i++) { Dataset slice = reorderedData.getSlice(new int[] { 0, i }, new int[] { dims[0], i + 1 }, null); slice.squeeze(); if (isDimAxis) { slice.setName(String.format("%s[%d]", dName, i)); } else { String z = lines == 1 && zaxis.getRank() == 0 ? zaxis.getString() : zaxis.getString(i); slice.setName(String.format("%s[%d=%s]", dName, i, z)); } yaxes[i] = slice; } try { if (plotStackIn3D) SDAPlotter.updateStackPlot(PLOTNAME, xaxes, yaxes, zaxis); else { SDAPlotter.updatePlot(PLOTNAME, dName, xaxes, yaxes); } } catch (Exception e) { logger.error("Could not plot 1d stack"); } break; case IMAGE: case SURFACE: reorderedData = slicedAndReorderData(monitor, slices, average, order, meta); if (isRankBad(reorderedData, 2)) { try { SDAPlotter.clearPlot(PLOTNAME); } catch (Exception e) { logger.error("Could not clear plot", e); } return; } if (meta != null) { reorderedData.setMetadata(meta); } try { IDataset xAxisSlice = make1DAxisSlice(slicedAxes, 0); IDataset yAxisSlice = make1DAxisSlice(slicedAxes, 1); if (itype == InspectorType.IMAGE) SDAPlotter.imagePlot(PLOTNAME, xAxisSlice, yAxisSlice, reorderedData); else SDAPlotter.surfacePlot(PLOTNAME, xAxisSlice, yAxisSlice, reorderedData); } catch (Exception e) { logger.error("Could not plot image or surface"); } break; case IMAGEXP: if (isExplorerNull()) return; pushImages(monitor, slices, order); break; case MULTIIMAGES: pushMultipleImages(monitor, sliceProperties, slices, slicedAxes, order); break; case VOLUME: reorderedData = slicedAndReorderData(monitor, slices, average, order, meta); if (isRankBad(reorderedData, 3)) { return; } try { SDAPlotter.volumePlot(VOLVIEWNAME, reorderedData); } catch (Exception e) { logger.error("Could not plot volume"); } break; case DATA1D: case DATA2D: case EMPTY: case POINTS1D: case POINTS2D: case POINTS3D: case HYPER: break; } } private boolean isExplorerNull() { if (explorerName == null || explorer == null) { site.getShell().getDisplay().syncExec(new Runnable() { @Override public void run() { try { explorer = (ImageExplorerView) site.getPage().showView(ImageExplorerView.ID, null, IWorkbenchPage.VIEW_CREATE); if (explorer != null) { explorerName = explorer.getPlotViewName(); } else { explorerName = null; } } catch (PartInitException e) { logger.error("Cannot find image explorer view"); e.printStackTrace(); } } }); } return explorerName == null; } private void pushImages(final IMonitor monitor, final Slice[] slices, final int[] order) { // work out slicing result final int[] shape = dataset.getShape(); // System.err.printf("Shape: %s; slicing: [%s]; order: %s\n", Arrays.toString(shape), Slice.createString(slices), Arrays.toString(order)); int rank = shape.length; int ns = slices.length; // dimensions for iterating over (order.length == slices.length) int ids = ns > 3 ? 2 : 1; final List<Integer> gridDimNumber = new ArrayList<Integer>(); final int[] gridShape = new int[ids]; for (int i = 0; i < ids; i++) { // After dimension reordering, first two dimensions should be an image size // followed by grid dimensions int o = order[i + 2]; gridDimNumber.add(o); gridShape[i] = slices[o].getNumSteps(); } try { // System.err.printf("Grid: %s\n", Arrays.toString(gridShape)); if (ids == 1) { SDAPlotter.setupNewImageGrid(explorerName, gridShape[0]); } else { SDAPlotter.setupNewImageGrid(explorerName, gridShape[1], gridShape[0]); } } catch (Exception e) { logger.warn("Problem with setting up image explorer", e); } // use position iterator ignoring first set of slicing axes int[] start = new int[rank]; int[] stop = new int[rank]; int[] step = new int[rank]; Slice.convertFromSlice(slices, shape, start, stop, step); List<Integer> ignoreAxesList = new ArrayList<Integer>(Arrays.asList(ArrayUtils.toObject(order))); ignoreAxesList.removeAll(gridDimNumber); int[] ignoreAxes = ArrayUtils.toPrimitive(ignoreAxesList.toArray(new Integer[0])); PositionIterator it = new PositionIterator(shape, start.clone(), stop.clone(), step, ignoreAxes); int[] pos = it.getPos(); int dimGridX = gridDimNumber.get(0); int gridX0 = start[dimGridX]; int dimGridY = (ids == 1) ? -1 : gridDimNumber.get(1); int gridY0 = (ids == 1) ? -1 : start[dimGridY]; try { setInspectionRunning(); boolean memoryOK = true; while (!memoryOK || it.hasNext()) { // short-cut iteration when low on memory try { for (int i = 0; i < ids; i++) { int o = gridDimNumber.get(i); int b = pos[o]; start[o] = b; stop[o] = b + 1; } Dataset slicedData = sliceData(monitor, start, stop, step); if (slicedData == null) return; // System.err.printf("Pos %s; start %s; stop %s; step %s; ", Arrays.toString(pos), Arrays.toString(start), Arrays.toString(stop), Arrays.toString(step)); // System.err.printf("Shape %s\n", Arrays.toString(slicedData.getShape())); Dataset reorderedData = slicedData.getTransposedView(order); reorderedData.setName(slicedData.getName()); reorderedData.squeeze(); if (reorderedData.getSize() < 1) return; reorderedData.setName(dataset.getName() + Arrays.toString(pos)); if (!canContinueInspection()) { return; } if (explorer.isStopped()) { stopInspection(); return; } if (ids == 1) { SDAPlotter.plotImageToGrid(explorerName, reorderedData, true); } else { int gridX = pos[dimGridX] - gridX0; int gridY = pos[dimGridY] - gridY0; SDAPlotter.plotImageToGrid(explorerName, reorderedData, gridX, gridY, true); } if (!memoryOK) logger.warn("... memory reduction successful"); memoryOK = true; } catch (OutOfMemoryError e) { if (!memoryOK) // only allow one GC per slice throw e; memoryOK = false; logger.warn("Ran out of memory: attempting to reduce memory used..."); System.gc(); // try again after memory reduction } } } catch (Exception e) { logger.warn("Problem with sending data to image explorer", e); } finally { stopInspection(); } } private void pushMultipleImages(final IMonitor monitor, List<SliceProperty> sliceProperties, final Slice[] slices, List<? extends IDataset> slicedAxes, final int[] order) { // work out slicing result int[] shape = dataset.getShape(); int smax = slices.length; if (smax < 2) smax = 2; final int sliceAxis = order[2]; final Slice[] subSlices = new Slice[smax]; for (int i = 0; i < smax; i++) { if (i < slices.length) { subSlices[i] = i == sliceAxis ? slices[i].clone() : slices[i]; } else { subSlices[i] = new Slice(shape[i]); } shape[i] = slices[i].getNumSteps(); } final int nimages = shape[sliceAxis]; if (nimages > MULTIIMAGESLIMIT) { logger.warn("Try plot too many images in multiple images plot: reduced from {} images to {}", nimages, MULTIIMAGESLIMIT); SliceProperty p = sliceProperties.get(sliceAxis); Slice s = p.getValue(); Integer st = s.getStart(); p.setStop((st == null ? 0 : st) + MULTIIMAGESLIMIT * s.getStep(), true); return; } IDataset yaxis = make1DAxisSlice(slicedAxes, 1); IDataset xaxis = make1DAxisSlice(slicedAxes, 0); try { Slice subSlice = subSlices[sliceAxis]; int start = subSlice.getStart() == null ? 0 : subSlice.getStart(); subSlices[sliceAxis].setStop(start + 1); setInspectionRunning(); IDataset[] images = new IDataset[nimages]; for (int i = 0; i < nimages; i++) { subSlices[sliceAxis].setPosition(start + i); Dataset slicedData = sliceData(monitor, subSlices); if (slicedData == null) return; Dataset reorderedData = slicedData.getTransposedView(order); reorderedData.setName(slicedData.getName()); reorderedData.squeeze(); if (reorderedData.getSize() < 1) return; reorderedData.setName(dataset.getName() + "." + i); if (!canContinueInspection()) { return; } images[i] = reorderedData; } SDAPlotter.imagesPlot(PLOTNAME, xaxis, yaxis, images); } catch (Exception e) { logger.warn("Problem with sending data to image explorer", e); } finally { stopInspection(); } } } /** * Straightforward dataset table tabs */ class DataTab extends PlotTab { public DataTab(IWorkbenchPartSite partSite, InspectorType type, String title, String[] axisNames) { super(partSite, type, title, axisNames); } @Override public void pushToView(IMonitor monitor, List<SliceProperty> sliceProperties) { if (dataset == null) return; Slice[] slices = new Slice[sliceProperties.size()]; boolean[] average = new boolean[sliceProperties.size()]; for (int i = 0; i < slices.length; i++) { slices[i] = sliceProperties.get(i).getValue(); average[i] = sliceProperties.get(i).isAverage(); } int[] order = getOrder(daxes.size()); final List<? extends IDataset> slicedAxes = sliceAxes(getChosenAxes(), slices, average, order); if (itype == InspectorType.DATA2D) { swapFirstTwoInOrder(order); } final Dataset reorderedData = slicedAndReorderData(monitor, slices, average, order, null); if (reorderedData == null) return; reorderedData.setName(dataset.getName()); reorderedData.squeeze(); if (reorderedData.getSize() < 1) return; switch (itype) { case DATA1D: if (isRankBad(reorderedData, 2)) return; final IDataset rAxisSlice = make1DAxisSlice(slicedAxes, 0); composite.getDisplay().asyncExec(new Runnable() { @Override public void run() { DatasetTableView tableView = getDatasetTableView(); if (tableView == null) return; tableView.setData(reorderedData.reshape(reorderedData.getShape()[0], 1), rAxisSlice, null); } }); break; case DATA2D: if (isRankBad(reorderedData, 2)) return; final IDataset yAxisSlice = make1DAxisSlice(slicedAxes, 0); final IDataset xAxisSlice = make1DAxisSlice(slicedAxes, 1); composite.getDisplay().asyncExec(new Runnable() { @Override public void run() { DatasetTableView tableView = getDatasetTableView(); if (tableView == null) return; tableView.setData(reorderedData, xAxisSlice, yAxisSlice); } }); break; case EMPTY: case IMAGE: case LINE: case LINESTACK: case IMAGEXP: case MULTIIMAGES: case POINTS1D: case POINTS2D: case POINTS3D: case SURFACE: case VOLUME: case HYPER: break; } } private DatasetTableView getDatasetTableView() { DatasetTableView view = null; // check if Dataset Table View is open try { view = (DatasetTableView) site.getPage().showView(DatasetTableView.ID, null, IWorkbenchPage.VIEW_CREATE); } catch (PartInitException e) { logger.error("All over now! Cannot find dataset table view: {} ", e); } return view; } } /** * Scatter point plots */ class ScatterTab extends PlotTab { private static final int POINTSIZE = 4; private static final String CONSTANT = "constant"; private static final String DATA = "data"; private boolean useData; // true if we want to use dataset values for size of points public ScatterTab(IWorkbenchPartSite partSite, InspectorType type, String title, String[] axisNames) { super(partSite, type, title, axisNames); comboOffset = 1; } @Override protected void createCombos(Composite cHolder, SelectionListener listener) { Label l = new Label(cHolder, SWT.NONE); l.setText("size"); axisLabels.add(l); Combo c = new Combo(cHolder, SWT.READ_ONLY); c.add(" "); c.addSelectionListener(listener); combos.add(c); super.createCombos(cHolder, listener); } @Override public boolean checkCompatible(ILazyDataset data) { boolean isCompatible = false; int rank = data.getRank(); if (rank == 1) isCompatible = true; else isCompatible = rank >= axes.length - 1; if (composite != null) composite.setEnabled(isCompatible); return isCompatible; } @Override public boolean canPlotConstant() { return true; } @Override public boolean[] getUsedDims() { boolean[] used = super.getUsedDims(); if (daxes != null && daxes.size() == 1) used[0] = true; return used; } @Override protected List<AxisChoice> getChosenAxes() { if (daxes != null && daxes.size() != 1) return super.getChosenAxes(); List<String> names = getChosenAxisNames(); List<AxisChoice> list = new ArrayList<AxisChoice>(); for (String n : names) { AxisChoice a = null; for (AxisSelection s : daxes) { a = s.getAxis(n); if (a != null) { break; } } if (a != null) { list.add(a); } else { logger.warn("No axis of names {} found in selections {}", names, daxes); } } return list; } protected LinkedList<String> getAllAxisNames() { // get all axis names for dimensions > 1 LinkedList<String> sAxes = new LinkedList<String>(); if (daxes != null) { for (AxisSelection a : daxes) { if (a.getLength() > 0) { for (int j = 0, jmax = a.size(); j < jmax; j++) { sAxes.add(a.getName(j)); } } } } return sAxes; } @Override protected void populateCombos() { Combo c = combos.get(0); c.removeAll(); c.add(CONSTANT); String name = dataset == null ? null : dataset.getName(); if (name == null || name.length() == 0) c.add(DATA); else c.add(name); c.setText(CONSTANT); if (paxes != null) { PlotAxisProperty p = paxes.get(0); p.setName(CONSTANT, false); } if (daxes != null && daxes.size() != 1) { super.populateCombos(); return; } int cSize = combos.size() - comboOffset; LinkedList<String> sAxes = getAllAxisNames(); int jmax = daxes.size(); for (int i = 0; i < cSize; i++) { c = combos.get(i + comboOffset); c.removeAll(); PlotAxisProperty p = paxes.get(i + comboOffset); String a; if (i < jmax) { a = daxes.get(i).getSelectedName(); if (!sAxes.contains(a)) { a = sAxes.getLast(); } } else { a = sAxes.getLast(); } p.clear(); int pmax = sAxes.size(); for (int j = 0; j < pmax; j++) { String n = sAxes.get(j); p.put(j, n); c.add(n); } c.setText(a); sAxes.remove(a); p.setName(a, false); p.setInSet(true); } } @Override protected void repopulateCombos(String oldName, String newName) { if (combos == null) return; if (daxes != null && daxes.size() != 1) { super.repopulateCombos(oldName, newName); return; } // cascade through plot axes strings and indices // reduce choice each time int cSize = combos.size() - comboOffset; LinkedList<String> sAxes = getAllAxisNames(); int jmax = daxes.size(); if (jmax == 0) return; boolean fromAxisSelection = oldName != null && newName != null; String a = null; for (int i = 0; i < cSize; i++) { Combo c = combos.get(i + comboOffset); a = (fromAxisSelection && i < jmax) ? daxes.get(i).getSelectedName() : c.getItem(c.getSelectionIndex()); c.removeAll(); if (!sAxes.contains(a)) { a = sAxes.get(0); } if (!fromAxisSelection) { if (i < jmax) daxes.get(i).selectAxis(a, false); } for (String p : sAxes) c.add(p); c.setText(a); sAxes.remove(a); if (paxes != null) { PlotAxisProperty p = paxes.get(i + comboOffset); if (p.isInSet()) { p.setName(a, false); } } } if (a != null && paxes != null) { if (paxes.get(cSize - 1 + comboOffset).isInSet()) { paxes.get(cSize - 1 + comboOffset).setName(a); } } } @Override protected List<Dataset> sliceAxes(List<AxisChoice> axes, Slice[] slices, boolean[] average, int[] order) { if (daxes.size() != 1) return super.sliceAxes(axes, slices, average, order); List<Dataset> slicedAxes = new ArrayList<Dataset>(); if (slices.length != 1) { logger.error("No slices defined"); return null; } Slice s = slices[0]; if (s != null) { for (AxisChoice a : axes) { slicedAxes.add(DatasetUtils.convertToDataset(a.getValues().getSlice(s))); } } return slicedAxes; } @Override public void pushToView(IMonitor monitor, List<SliceProperty> sliceProperties) { if (dataset == null) return; useData = !CONSTANT.equals(paxes.get(0).getName()); Slice[] slices = new Slice[sliceProperties.size()]; boolean[] average = new boolean[sliceProperties.size()]; for (int i = 0; i < slices.length; i++) { slices[i] = sliceProperties.get(i).getValue(); average[i] = sliceProperties.get(i).isAverage(); } List<AxisChoice> axes = getChosenAxes(); int rank = daxes.size(); int[] order = getOrder(rank); List<Dataset> slicedAxes = sliceAxes(axes, slices, average, order); if (slicedAxes == null) return; Dataset reorderedData = slicedAndReorderData(monitor, slices, average, order, null); if (reorderedData == null) return; // TODO cope with axis datasets that are >1 dimensions Dataset x; IDataset y; switch (itype) { case POINTS1D: x = slicedAxes.get(0); y = reorderedData.flatten(); if (!x.isCompatibleWith(y)) { logger.error("Could not match axis to data for scatter plot"); return; } IDataset size = useData ? y : new IntegerDataset(x.getSize()).fill(POINTSIZE); try { SDAPlotter.scatter2DPlot(PLOTNAME, x.flatten(), y, size); } catch (Exception e) { logger.error("Could not plot 1d points"); return; } break; case POINTS2D: if (!useData) { // TODO >1D dataset x = slicedAxes.get(0).flatten(); y = slicedAxes.get(1).flatten(); int length = Math.min(x.getSize(), y.getSize()); Slice slice = new Slice(length); reorderedData = new IntegerDataset(length).fill(POINTSIZE); try { SDAPlotter.scatter2DPlot(PLOTNAME, x.getSlice(slice), y.getSlice(slice), reorderedData); } catch (Exception e) { logger.error("Could not plot 2d points"); return; } } else { if (reorderedData.getRank() == 1) { x = slicedAxes.get(0); y = slicedAxes.get(1); } else { List<? extends Dataset> grid = DatasetUtils.meshGrid(slicedAxes.get(0), slicedAxes.get(1)); x = grid.get(0); y = grid.get(1); } if (!reorderedData.isCompatibleWith(x) || !reorderedData.isCompatibleWith(y)) { logger.error("Could not match axes to data for scatter plot"); return; } try { SDAPlotter.scatter2DPlot(PLOTNAME, x.flatten(), ((Dataset) y).flatten(), reorderedData.flatten()); } catch (Exception e) { logger.error("Could not plot 2d points"); return; } } break; case POINTS3D: if (!useData) { // TODO >1D dataset x = slicedAxes.get(0).flatten(); y = slicedAxes.get(1).flatten(); Dataset z = slicedAxes.get(2).flatten(); int length = Math.min(x.getSize(), y.getSize()); length = Math.min(length, z.getSize()); Slice slice = new Slice(length); reorderedData = new IntegerDataset(length).fill(POINTSIZE); try { SDAPlotter.scatter3DPlot(PLOTNAME, x.getSlice(slice), y.getSlice(slice), z.getSlice(slice), reorderedData); } catch (Exception e) { logger.error("Could not plot 3d points"); return; } } else { IDataset z; x = DatasetUtils.convertToDataset(axes.get(0).getValues()); y = DatasetUtils.convertToDataset(axes.get(1).getValues()); z = DatasetUtils.convertToDataset(axes.get(2).getValues()); if (reorderedData.getRank() == 1) { } else { List<? extends Dataset> grid = DatasetUtils.meshGrid(x, (Dataset) y, (Dataset) z); x = grid.get(0); y = grid.get(1); z = grid.get(2); } if (!reorderedData.isCompatibleWith(x) || !reorderedData.isCompatibleWith(y) || !reorderedData.isCompatibleWith(z)) { logger.error("Could not match axes to data for scatter plot"); return; } try { SDAPlotter.scatter3DPlot(PLOTNAME, x.flatten(), ((Dataset) y).flatten(), ((Dataset) z).flatten(), reorderedData.flatten()); } catch (Exception e) { logger.error("Could not plot 3d points"); return; } } break; case DATA1D: case DATA2D: case EMPTY: case IMAGE: case LINE: case LINESTACK: case IMAGEXP: case MULTIIMAGES: case SURFACE: case VOLUME: case HYPER: break; } } }