diff --git a/view-simulation-results/pom.xml b/view-simulation-results/pom.xml index 9b27f8b..d01e897 100644 --- a/view-simulation-results/pom.xml +++ b/view-simulation-results/pom.xml @@ -11,7 +11,7 @@ view-simulation-results - 1.0-SNAPSHOT + 2.0 VCell View Simulation Results Virtual Cell ImageJ plugin to retrieve VCell simulation results for analysis within ImageJ @@ -159,39 +159,36 @@ 4.1.1 - - - org.jsoup - jsoup - 1.16.1 - - org.scijava scijava-log-slf4j - 1.0.6 + org.slf4j slf4j-api - + org.slf4j slf4j-simple - com.google.code.gson gson - 2.10.1 + software.amazon.awssdk s3 + + com.opencsv + opencsv + + diff --git a/view-simulation-results/src/main/java/org/vcell/N5/N5ImageHandler.java b/view-simulation-results/src/main/java/org/vcell/N5/N5ImageHandler.java index b2695e0..09ef90e 100644 --- a/view-simulation-results/src/main/java/org/vcell/N5/N5ImageHandler.java +++ b/view-simulation-results/src/main/java/org/vcell/N5/N5ImageHandler.java @@ -11,8 +11,7 @@ import org.scijava.plugin.Parameter; import org.scijava.plugin.Plugin; import org.vcell.N5.UI.MainPanel; -import org.vcell.N5.UI.N5ExportTable; -import org.vcell.N5.retrieving.LoadingFactory; +import org.vcell.N5.retrieving.LoadingManager; import org.vcell.N5.retrieving.SimResultsLoader; import java.io.*; @@ -34,13 +33,14 @@ public class N5ImageHandler implements Command { public static MainPanel exportTable; public static String exportedMetaDataPath = System.getProperty("user.home") + "/.vcell/exportMetaData.json"; private static ExportDataRepresentation.FormatExportDataRepresentation exampleJSONData; - public static LoadingFactory loadingFactory; + public static LoadingManager loadingManager; @Override public void run() { initializeLogService(); - loadingFactory = new LoadingFactory(); + loadingManager = new LoadingManager(); exportTable = new MainPanel(); + MainPanel.controlButtonsPanel.setStateToInitializing(true); setExampleJSONData(); // N5ImageHandler.logService.setLevel(LogService.DEBUG); Thread thread = new Thread(() -> { @@ -48,6 +48,8 @@ public void run() { // So create one upon initialization, while the user is focused on the GUI // and by the time they open an Image it's already loaded. SimResultsLoader.s3ClientBuilder = AmazonS3ClientBuilder.standard(); + MainPanel.controlButtonsPanel.setStateToInitializing(false); + MainPanel.n5ExportTable.valueChanged(null); }); thread.start(); } diff --git a/view-simulation-results/src/main/java/org/vcell/N5/UI/AdvancedFeatures.java b/view-simulation-results/src/main/java/org/vcell/N5/UI/AdvancedFeatures.java new file mode 100644 index 0000000..62c6152 --- /dev/null +++ b/view-simulation-results/src/main/java/org/vcell/N5/UI/AdvancedFeatures.java @@ -0,0 +1,35 @@ +package org.vcell.N5.UI; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.EtchedBorder; +import java.awt.*; + +public class AdvancedFeatures extends JPanel { + public final JButton openInMemory; + public final JButton copyLink; + public final JButton useN5Link; + + + public AdvancedFeatures(){ + Border lowerEtchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED); + + + openInMemory = new JButton("Open In Memory"); + JPanel checkBoxPanel = new JPanel(); +// checkBoxPanel.add(openInMemory); + + JPanel buttonPanel = new JPanel(); + copyLink = new JButton("Copy Link"); + useN5Link = new JButton("Use N5 Link"); + buttonPanel.add(copyLink); + buttonPanel.add(useN5Link); + buttonPanel.add(openInMemory); + +// setLayout(new BorderLayout()); +// add(buttonPanel, BorderLayout.NORTH); +// add(checkBoxPanel, BorderLayout.SOUTH); + add(buttonPanel); + this.setBorder(BorderFactory.createTitledBorder(lowerEtchedBorder, " Advanced Features ")); + } +} diff --git a/view-simulation-results/src/main/java/org/vcell/N5/UI/ControlButtonsPanel.java b/view-simulation-results/src/main/java/org/vcell/N5/UI/ControlButtonsPanel.java index 9eb0579..ebbc9fb 100644 --- a/view-simulation-results/src/main/java/org/vcell/N5/UI/ControlButtonsPanel.java +++ b/view-simulation-results/src/main/java/org/vcell/N5/UI/ControlButtonsPanel.java @@ -1,6 +1,7 @@ package org.vcell.N5.UI; import org.vcell.N5.N5ImageHandler; +import org.vcell.N5.retrieving.SimResultsLoader; import javax.swing.*; import javax.swing.border.Border; @@ -8,60 +9,57 @@ import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.time.LocalDateTime; -import java.util.Enumeration; public class ControlButtonsPanel extends JPanel implements ActionListener { private static JButton openOrCancel; + private final String openButtonText = "Open Virtual Stack"; + private final String cancelButtonText = "Cancel"; + + private final JButton dataReduction; + private final String runScriptButtonText = "Run Measurement Script"; + private final String cancelScriptButtonText = "Cancel Measurement Script"; // private final JButton openLocal = new JButton("Open N5 Local"); - private final JButton copyLink; - private final JButton useN5Link; private final JButton questionMark; - private final JButton openInMemory; - private final JCheckBox includeExampleExports; - private final JCheckBox todayInterval; - private final JCheckBox monthInterval; - private final JCheckBox yearlyInterval; - private final JCheckBox anyInterval; - private final JPanel timeFilter; + + public final JCheckBox includeExampleExports; + public final JCheckBox displayAdvancedFeatures; private N5ExportTable n5ExportTable; private RemoteFileSelection remoteFileSelection; + public final AdvancedFeatures advancedFeatures = new AdvancedFeatures(); + private PanelState panelState = PanelState.NOTHING_OR_LOADING_IMAGE; public ControlButtonsPanel(){ - openOrCancel = new JButton("Open"); - copyLink = new JButton("Copy Link"); - useN5Link = new JButton("Use N5 Link"); - questionMark = new JButton("?"); - questionMark.setPreferredSize(new Dimension(20, 20)); - openInMemory = new JButton("Open In Memory"); - openInMemory.setSelected(false); includeExampleExports = new JCheckBox("Show Example Exports"); includeExampleExports.setSelected(!N5ImageHandler.exportedDataExists()); + displayAdvancedFeatures = new JCheckBox("Advanced Features"); + + openOrCancel = new JButton("Open Virtual Stack"); + dataReduction = new JButton(runScriptButtonText); + questionMark = new JButton("?"); + questionMark.setPreferredSize(new Dimension(20, 20)); + GridBagConstraints gridBagConstraints = new GridBagConstraints(); JPanel topRow = new JPanel(new GridBagLayout()); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 0; topRow.add(openOrCancel, gridBagConstraints); - gridBagConstraints.gridwidth = 1; gridBagConstraints.gridx = 1; - gridBagConstraints.gridy = 0; - topRow.add(openInMemory, gridBagConstraints); - gridBagConstraints.gridx = 2; + topRow.add(dataReduction, gridBagConstraints); + + JPanel bottomRow = new JPanel(new GridBagLayout()); - gridBagConstraints.gridx = 0; - gridBagConstraints.gridy = 0; - bottomRow.add(copyLink, gridBagConstraints); + bottomRow.add(includeExampleExports); gridBagConstraints.gridx = 1; - bottomRow.add(useN5Link, gridBagConstraints); + bottomRow.add(displayAdvancedFeatures, gridBagConstraints); + gridBagConstraints.gridx = 2; bottomRow.add(questionMark); - JPanel userButtonsPanel = new JPanel(new GridBagLayout()); gridBagConstraints.gridx = 0; gridBagConstraints.gridy = 0; @@ -71,58 +69,32 @@ public ControlButtonsPanel(){ // buttonsPanel.add(questionMark); - - todayInterval = new JCheckBox("Past 24 Hours"); - monthInterval = new JCheckBox("Past Month"); - yearlyInterval = new JCheckBox("Past Year"); - anyInterval = new JCheckBox("Any Time"); - anyInterval.setSelected(true); - - ButtonGroup buttonGroup = new ButtonGroup(); - buttonGroup.add(todayInterval); - buttonGroup.add(monthInterval); - buttonGroup.add(yearlyInterval); - buttonGroup.add(anyInterval); - - JPanel filters = new JPanel(); - filters.setLayout(new BorderLayout()); - timeFilter = new JPanel(new GridBagLayout()); - timeFilter.add(anyInterval); - timeFilter.add(todayInterval); - timeFilter.add(monthInterval); - timeFilter.add(yearlyInterval); -// timeFilter.setBorder(BorderFactory.createTitledBorder(lowerEtchedBorder, " Time ")); - filters.add(timeFilter, BorderLayout.NORTH); - filters.add(includeExampleExports, BorderLayout.SOUTH); - Border lowerEtchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED); - filters.setBorder(BorderFactory.createTitledBorder(lowerEtchedBorder, " Filters ")); - - int paneWidth = 800; - this.setPreferredSize(new Dimension(paneWidth, 100)); + this.setPreferredSize(new Dimension(paneWidth, 110)); this.setLayout(new BorderLayout()); // topBar.add(openLocal); + Border lowerEtchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED); this.add(userButtonsPanel, BorderLayout.EAST); - this.add(filters, BorderLayout.WEST); this.setBorder(BorderFactory.createTitledBorder(lowerEtchedBorder, " User Options ")); + advancedFeatures.setVisible(false); + add(advancedFeatures, BorderLayout.WEST); + openOrCancel.addActionListener(this); - copyLink.addActionListener(this); + advancedFeatures.copyLink.addActionListener(this); questionMark.addActionListener(this); - useN5Link.addActionListener(this); - includeExampleExports.addActionListener(this); + advancedFeatures.useN5Link.addActionListener(this); // openLocal.addActionListener(this); - openInMemory.addActionListener(this); + includeExampleExports.addActionListener(this); + displayAdvancedFeatures.addActionListener(this); + dataReduction.addActionListener(this); + advancedFeatures.openInMemory.addActionListener(this); - Enumeration b = buttonGroup.getElements(); - while (b.hasMoreElements()){ - b.nextElement().addActionListener(this); - } openOrCancel.setEnabled(false); - copyLink.setEnabled(false); - openInMemory.setEnabled(false); + dataReduction.setEnabled(false); + advancedFeatures.copyLink.setEnabled(false); } public void initialize(N5ExportTable n5ExportTable, RemoteFileSelection remoteFileSelection){ @@ -132,72 +104,112 @@ public void initialize(N5ExportTable n5ExportTable, RemoteFileSelection remoteFi @Override public void actionPerformed(ActionEvent e) { - if(e.getSource().equals(openOrCancel) || e.getSource().equals(openInMemory)){ - if (openOrCancel.getText().equals("Cancel")){ - n5ExportTable.removeFromLoadingRows(); + boolean inMemory = e.getSource().equals(advancedFeatures.openInMemory); + if(e.getSource().equals(openOrCancel) || inMemory){ + panelState = PanelState.NOTHING_OR_LOADING_IMAGE; + if (openOrCancel.getText().equals(cancelButtonText)){ + n5ExportTable.stopSelectedImageFromLoading(); + updateButtonsToMatchState(false); } else { - n5ExportTable.openSelectedRows(e.getSource().equals(openInMemory)); + n5ExportTable.openSelectedRows(inMemory, false, SimResultsLoader.OpenTag.VIEW); + updateButtonsToMatchState(true); + } + } else if (e.getSource().equals(dataReduction)) { + if (dataReduction.getText().equals(cancelScriptButtonText)){ + panelState = PanelState.NOTHING_OR_LOADING_IMAGE; + N5ImageHandler.loadingManager.stopAllImagesAndAnalysis(); + } else{ + panelState = PanelState.PERFORMING_ANALYSIS; + setButtonsToCancelReduction(); + n5ExportTable.openSelectedRows(false, true, SimResultsLoader.OpenTag.DATA_REDUCTION); } - } else if (e.getSource().equals(copyLink)) { + updateButtonsToMatchState(); + } else if (e.getSource().equals(advancedFeatures.copyLink)) { n5ExportTable.copySelectedRowLink(); } else if (e.getSource().equals(questionMark)) { new HelpExplanation().displayHelpMenu(); - } else if (e.getSource().equals(useN5Link)) { + } else if (e.getSource().equals(advancedFeatures.useN5Link)) { remoteFileSelection.setVisible(true); } else if (e.getSource().equals(includeExampleExports)){ - if(includeExampleExports.isSelected()){ - n5ExportTable.updateExampleExportsToTable(); - return; - } - n5ExportTable.updateTableData(); - } else if (e.getSource().equals(anyInterval) || e.getSource().equals(todayInterval) - || e.getSource().equals(monthInterval) || e.getSource().equals(yearlyInterval)) { - if(includeExampleExports.isSelected()){ - n5ExportTable.updateExampleExportsToTable(); - return; - } n5ExportTable.updateTableData(); + } else if (e.getSource().equals(displayAdvancedFeatures)) { + advancedFeatures.setVisible(displayAdvancedFeatures.isSelected()); } } - public LocalDateTime oldestTimeAllowed(){ - LocalDateTime pastTime = LocalDateTime.now(); - if (todayInterval.isSelected()){ - pastTime = pastTime.minusDays(1); - } else if (monthInterval.isSelected()) { - pastTime = pastTime.minusMonths(1); - } else if (yearlyInterval.isSelected()) { - pastTime = pastTime.minusYears(1); - } else { - pastTime = pastTime.minusYears(10); //Max date back is 10 years + public void updateButtonsToMatchState(){ + updateButtonsToMatchState(false); + } + + public void updateButtonsToMatchState(boolean rowIsLoadingImage){ + updateButtonsToMatchState(rowIsLoadingImage, panelState); + } + + public void setStateToInitializing(boolean isInitializing){ + panelState = isInitializing ? PanelState.INITIALIZING : PanelState.NOTHING_OR_LOADING_IMAGE; + } + + public void updateButtonsToMatchState(boolean rowIsLoadingImage, PanelState newPanelState){ + switch (newPanelState){ + case NOTHING_OR_LOADING_IMAGE: + if (rowIsLoadingImage){ + allowCancel(); + } else { + enableAllButtons(true); + } + break; + case PERFORMING_ANALYSIS: + setButtonsToCancelReduction(); + break; + case INITIALIZING: + enableAllButtons(false); + break; } - return pastTime; + panelState = newPanelState; + } + + public void setButtonsToCancelReduction(){ + openOrCancel.setText(openButtonText); + openOrCancel.setEnabled(false); + advancedFeatures.useN5Link.setEnabled(false); + advancedFeatures.openInMemory.setEnabled(false); + + advancedFeatures.copyLink.setEnabled(true); + dataReduction.setText(cancelScriptButtonText); } - public void allowCancel(boolean allow){ + private void allowCancel(){ openOrCancel.setEnabled(true); - copyLink.setEnabled(true); - openInMemory.setEnabled(!allow); - useN5Link.setEnabled(true); + advancedFeatures.copyLink.setEnabled(true); + advancedFeatures.useN5Link.setEnabled(true); remoteFileSelection.submitS3Info.setEnabled(true); - if (allow){ - openOrCancel.setText("Cancel"); - } else { - openOrCancel.setText("Open"); - } + dataReduction.setEnabled(false); + advancedFeatures.openInMemory.setEnabled(false); + openOrCancel.setText(cancelButtonText); } - public void enableRowContextDependentButtons(boolean enable){ - openOrCancel.setEnabled(enable); - copyLink.setEnabled(enable); - openInMemory.setEnabled(enable); + public void disableAllContextDependentButtons(){ + openOrCancel.setEnabled(false); + advancedFeatures.copyLink.setEnabled(false); + dataReduction.setEnabled(false); + advancedFeatures.openInMemory.setEnabled(false); } - public void enableCriticalButtons(boolean enable){ - useN5Link.setEnabled(enable); + public void enableAllButtons(boolean enable){ + openOrCancel.setText(openButtonText); + dataReduction.setText(runScriptButtonText); + + advancedFeatures.useN5Link.setEnabled(enable); openOrCancel.setEnabled(enable); - copyLink.setEnabled(enable); + advancedFeatures.copyLink.setEnabled(enable); remoteFileSelection.submitS3Info.setEnabled(enable); - openInMemory.setEnabled(enable); + dataReduction.setEnabled(enable); + advancedFeatures.openInMemory.setEnabled(enable); + } + + public enum PanelState { + PERFORMING_ANALYSIS, + NOTHING_OR_LOADING_IMAGE, + INITIALIZING } } diff --git a/view-simulation-results/src/main/java/org/vcell/N5/UI/Filters/SearchBar.java b/view-simulation-results/src/main/java/org/vcell/N5/UI/Filters/SearchBar.java new file mode 100644 index 0000000..cc0db5d --- /dev/null +++ b/view-simulation-results/src/main/java/org/vcell/N5/UI/Filters/SearchBar.java @@ -0,0 +1,37 @@ +package org.vcell.N5.UI.Filters; + +import org.vcell.N5.UI.MainPanel; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.KeyEvent; +import java.awt.event.KeyListener; + +public class SearchBar extends JPanel implements KeyListener { + public static final JTextField searchTextField = new JTextField(); + + public SearchBar(){ + this.add(new JLabel("Search: ")); + searchTextField.setPreferredSize(new Dimension(600, 20)); + searchTextField.addKeyListener(this); + this.add(searchTextField); +// this.setPreferredSize(new Dimension(600, 20)); + } + + @Override + public void keyTyped(KeyEvent e) { + + } + + @Override + public void keyPressed(KeyEvent e) { + + } + + @Override + public void keyReleased(KeyEvent e) { + if (e.getSource().equals(searchTextField)){ + MainPanel.n5ExportTable.updateTableData(searchTextField.getText()); + } + } +} diff --git a/view-simulation-results/src/main/java/org/vcell/N5/UI/Filters/TimeFilter.java b/view-simulation-results/src/main/java/org/vcell/N5/UI/Filters/TimeFilter.java new file mode 100644 index 0000000..6a29fd7 --- /dev/null +++ b/view-simulation-results/src/main/java/org/vcell/N5/UI/Filters/TimeFilter.java @@ -0,0 +1,81 @@ +package org.vcell.N5.UI.Filters; + +import org.vcell.N5.UI.MainPanel; +import org.vcell.N5.UI.N5ExportTable; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.EtchedBorder; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.time.LocalDateTime; +import java.util.Enumeration; + + +public class TimeFilter extends JPanel implements ActionListener { + private final JCheckBox todayInterval; + private final JCheckBox monthInterval; + private final JCheckBox yearlyInterval; + private final JCheckBox anyInterval; + private final JPanel timeFilter; + + private final N5ExportTable n5ExportTable; + + + public TimeFilter(){ + n5ExportTable = MainPanel.n5ExportTable; + + todayInterval = new JCheckBox("Past 24 Hours"); + monthInterval = new JCheckBox("Past Month"); + yearlyInterval = new JCheckBox("Past Year"); + anyInterval = new JCheckBox("Any Time"); + anyInterval.setSelected(true); + + ButtonGroup buttonGroup = new ButtonGroup(); + buttonGroup.add(todayInterval); + buttonGroup.add(monthInterval); + buttonGroup.add(yearlyInterval); + buttonGroup.add(anyInterval); + + this.setLayout(new BorderLayout()); + timeFilter = new JPanel(new GridBagLayout()); + timeFilter.add(anyInterval); + timeFilter.add(todayInterval); + timeFilter.add(monthInterval); + timeFilter.add(yearlyInterval); +// timeFilter.setBorder(BorderFactory.createTitledBorder(lowerEtchedBorder, " Time ")); + this.add(timeFilter, BorderLayout.NORTH); + + Enumeration b = buttonGroup.getElements(); + while (b.hasMoreElements()){ + b.nextElement().addActionListener(this); + } + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource().equals(anyInterval) || e.getSource().equals(todayInterval) + || e.getSource().equals(monthInterval) || e.getSource().equals(yearlyInterval)) { + if(MainPanel.controlButtonsPanel.includeExampleExports.isSelected()){ + n5ExportTable.updateTableData(); + return; + } + n5ExportTable.updateTableData(); + } + } + + public LocalDateTime oldestTimeAllowed(){ + LocalDateTime pastTime = LocalDateTime.now(); + if (todayInterval.isSelected()){ + pastTime = pastTime.minusDays(1); + } else if (monthInterval.isSelected()) { + pastTime = pastTime.minusMonths(1); + } else if (yearlyInterval.isSelected()) { + pastTime = pastTime.minusYears(1); + } else { + pastTime = pastTime.minusYears(10); //Max date back is 10 years + } + return pastTime; + } +} diff --git a/view-simulation-results/src/main/java/org/vcell/N5/UI/HintTextField.java b/view-simulation-results/src/main/java/org/vcell/N5/UI/HintTextField.java new file mode 100644 index 0000000..4fa9cff --- /dev/null +++ b/view-simulation-results/src/main/java/org/vcell/N5/UI/HintTextField.java @@ -0,0 +1,43 @@ +package org.vcell.N5.UI; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.FocusAdapter; +import java.awt.event.FocusEvent; + +public class HintTextField extends JTextField { + Font gainFont = new Font("Tahoma", Font.PLAIN, 11); + Font lostFont = new Font("Tahoma", Font.ITALIC, 11); + public HintTextField(final String hint) { + setText(hint); + setFont(lostFont); + setForeground(Color.GRAY); + + this.addFocusListener(new FocusAdapter() { + + @Override + public void focusGained(FocusEvent e) { + if (getText().equals(hint)) { + setText(""); + setFont(gainFont); + } else { + setText(getText()); + setFont(gainFont); + } + } + + @Override + public void focusLost(FocusEvent e) { + if (getText().equals(hint)|| getText().isEmpty()) { + setText(hint); + setFont(lostFont); + setForeground(Color.GRAY); + } else { + setText(getText()); + setFont(gainFont); + setForeground(Color.BLACK); + } + } + }); + } +} diff --git a/view-simulation-results/src/main/java/org/vcell/N5/UI/MainPanel.java b/view-simulation-results/src/main/java/org/vcell/N5/UI/MainPanel.java index a49f5b7..5d4bb24 100644 --- a/view-simulation-results/src/main/java/org/vcell/N5/UI/MainPanel.java +++ b/view-simulation-results/src/main/java/org/vcell/N5/UI/MainPanel.java @@ -1,38 +1,52 @@ package org.vcell.N5.UI; +import org.vcell.N5.UI.Filters.SearchBar; +import org.vcell.N5.UI.Filters.TimeFilter; + import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import javax.swing.border.EtchedBorder; import javax.swing.table.DefaultTableCellRenderer; import javax.swing.table.TableColumn; import java.awt.*; import java.util.Enumeration; public class MainPanel { - private static JDialog exportTableDialog; - private final int paneWidth = 800; + public static JFrame exportTableDialog; public final static ControlButtonsPanel controlButtonsPanel = new ControlButtonsPanel(); - public final N5ExportTable n5ExportTable = new N5ExportTable(); + public final static N5ExportTable n5ExportTable = new N5ExportTable(); public final ExportDetailsPanel exportDetailsPanel = new ExportDetailsPanel(); public final RemoteFileSelection remoteFileSelection = new RemoteFileSelection(); - + public final static TimeFilter timeFilter = new TimeFilter(); + public final static SearchBar searchBar = new SearchBar(); public MainPanel(){ - JPanel parentPanel = new JPanel(); - - - n5ExportTable.initialize(controlButtonsPanel, exportDetailsPanel); + n5ExportTable.initialize(controlButtonsPanel, exportDetailsPanel, timeFilter); controlButtonsPanel.initialize(n5ExportTable, remoteFileSelection); - parentPanel.setLayout(new BorderLayout()); - parentPanel.add(controlButtonsPanel, BorderLayout.NORTH); + JPanel contentPanel = new JPanel(new BorderLayout()); + JPanel northPanel = new JPanel(new BorderLayout()); + northPanel.add(controlButtonsPanel, BorderLayout.SOUTH); + JSplitPane jSplitPane = new JSplitPane(JSplitPane.VERTICAL_SPLIT, n5ExportTable, exportDetailsPanel); jSplitPane.setContinuousLayout(true); - parentPanel.add(jSplitPane, BorderLayout.CENTER); - parentPanel.setPreferredSize(new Dimension(paneWidth, 650)); - JOptionPane pane = new JOptionPane(parentPanel, JOptionPane.PLAIN_MESSAGE, 0, null, new Object[]{"Close"}); - exportTableDialog = pane.createDialog("VCell Exports"); - exportTableDialog.setModal(false); + JPanel filters = new JPanel(new BorderLayout()); + Border lowerEtchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED); + filters.setBorder(BorderFactory.createTitledBorder(lowerEtchedBorder, " Filters ")); + filters.add(timeFilter, BorderLayout.NORTH); + filters.add(searchBar, BorderLayout.SOUTH); + + contentPanel.add(northPanel, BorderLayout.NORTH); + contentPanel.add(jSplitPane, BorderLayout.CENTER); + contentPanel.add(filters, BorderLayout.SOUTH); + contentPanel.setBorder(new EmptyBorder(15, 12, 15, 12)); + + exportTableDialog = new JFrame("VCell Exports"); + exportTableDialog.add(contentPanel); + exportTableDialog.pack(); exportTableDialog.setResizable(true); exportTableDialog.setVisible(true); } diff --git a/view-simulation-results/src/main/java/org/vcell/N5/UI/N5ExportTable.java b/view-simulation-results/src/main/java/org/vcell/N5/UI/N5ExportTable.java index f9de4e0..ca79296 100644 --- a/view-simulation-results/src/main/java/org/vcell/N5/UI/N5ExportTable.java +++ b/view-simulation-results/src/main/java/org/vcell/N5/UI/N5ExportTable.java @@ -3,6 +3,8 @@ import org.scijava.log.Logger; import org.vcell.N5.ExportDataRepresentation; import org.vcell.N5.N5ImageHandler; +import org.vcell.N5.UI.Filters.SearchBar; +import org.vcell.N5.UI.Filters.TimeFilter; import org.vcell.N5.retrieving.SimLoadingListener; import org.vcell.N5.retrieving.SimResultsLoader; @@ -23,7 +25,7 @@ import java.util.concurrent.TimeUnit; public class N5ExportTable extends JScrollPane implements ListSelectionListener, SimLoadingListener { - private N5ExportTableModel n5ExportTableModel; + public N5ExportTableModel n5ExportTableModel; private JTable exportListTable; private final Border lowerEtchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED); @@ -32,16 +34,19 @@ public class N5ExportTable extends JScrollPane implements ListSelectionListener, private ControlButtonsPanel controlPanel; private ExportDetailsPanel exportDetailsPanel; + private TimeFilter timeFilter; private final Logger logger = N5ImageHandler.getLogger(N5ExportTable.class); public N5ExportTable(){} - public void initialize(ControlButtonsPanel controlButtonsPanel, ExportDetailsPanel exportDetailsPanel){ + public void initialize(ControlButtonsPanel controlButtonsPanel, ExportDetailsPanel exportDetailsPanel, + TimeFilter timeFilter){ this.controlPanel = controlButtonsPanel; this.exportDetailsPanel = exportDetailsPanel; - N5ImageHandler.loadingFactory.addSimLoadingListener(this); + this.timeFilter = timeFilter; + N5ImageHandler.loadingManager.addSimLoadingListener(this); n5ExportTableModel = new N5ExportTableModel(); exportListTable = new JTable(n5ExportTableModel); this.setViewportView(exportListTable); @@ -71,38 +76,41 @@ public Component getTableCellRendererComponent(JTable table, Object value, boole this.setBorder(BorderFactory.createTitledBorder(lowerEtchedBorder, "Export Table")); exportListTable.getSelectionModel().addListSelectionListener(this); - if(!N5ImageHandler.exportedDataExists()){ - updateExampleExportsToTable(); - } - else{ - updateTableData(); - } + updateTableData(""); automaticRefresh(); } - void updateTableData(){ + public void updateTableData(){ + updateTableData(SearchBar.searchTextField.getText()); + } + + public void updateTableData(String strFilter){ // when initializing it is null if (controlPanel == null){ - updateTableData(LocalDateTime.now().minusYears(10)); + updateTableData(LocalDateTime.now().minusYears(10), strFilter); } else { - updateTableData(controlPanel.oldestTimeAllowed()); + updateTableData(timeFilter.oldestTimeAllowed(), strFilter); } } - void updateTableData(LocalDateTime oldestTimeAllowed){ + void updateTableData(LocalDateTime oldestTimeAllowed, String strFilter){ n5ExportTableModel.resetData(); - this.setBorder(BorderFactory.createTitledBorder(lowerEtchedBorder, "Personal Exports")); + if (!controlPanel.includeExampleExports.isSelected()){ + this.setBorder(BorderFactory.createTitledBorder(lowerEtchedBorder, "Personal Exports")); + } else { + this.setBorder(exampleBorder); + } try { - ExportDataRepresentation.FormatExportDataRepresentation formatExportData = N5ImageHandler.getJsonData(); - if (formatExportData != null){ - Stack jobStack = formatExportData.formatJobIDs; - while (!jobStack.isEmpty()){ - String jobID = jobStack.pop(); - if (!n5ExportTableModel.appendRowData(formatExportData.simulationDataMap.get(jobID), oldestTimeAllowed)){ - break; - } - } + ExportDataRepresentation.FormatExportDataRepresentation formatExportData = N5ImageHandler.exportedDataExists() && !controlPanel.includeExampleExports.isSelected() ? + N5ImageHandler.getJsonData() : N5ImageHandler.getExampleJSONData(); + + Stack jobStack = (Stack) formatExportData.formatJobIDs.clone(); + while (!jobStack.isEmpty()){ + String jobID = jobStack.pop(); + n5ExportTableModel.addToRowData(formatExportData.simulationDataMap.get(jobID), oldestTimeAllowed, + strFilter, true); } + n5ExportTableModel.fireTableDataChanged(); this.updateUI(); } catch (FileNotFoundException e) { @@ -110,41 +118,12 @@ void updateTableData(LocalDateTime oldestTimeAllowed){ } } - void updateExampleExportsToTable(){ - // when initializing it is null - if (controlPanel == null){ - updateExampleExportsToTable(LocalDateTime.now().minusYears(10)); - } else { - updateExampleExportsToTable(controlPanel.oldestTimeAllowed()); - } - } - - void updateExampleExportsToTable(LocalDateTime oldestTimeAllowed){ - n5ExportTableModel.resetData(); - this.setBorder(exampleBorder); - try{ - ExportDataRepresentation.FormatExportDataRepresentation exampleFormatExportData = N5ImageHandler.getExampleJSONData(); - Stack exampleJobStack = (Stack) exampleFormatExportData.formatJobIDs.clone(); - while (!exampleJobStack.isEmpty()){ - String jobID = exampleJobStack.pop(); - if (!n5ExportTableModel.appendRowData(exampleFormatExportData.simulationDataMap.get(jobID), oldestTimeAllowed)){ - break; - } - } - n5ExportTableModel.fireTableDataChanged(); - this.updateUI(); - } - catch (FileNotFoundException e){ - throw new RuntimeException("Can't open example N5 export table.", e); - } - } - private void automaticRefresh(){ Thread refreshTableThread = new Thread(() -> { try { while(true){ ExportDataRepresentation.FormatExportDataRepresentation formatExportData = N5ImageHandler.getJsonData(); - if (formatExportData != null && !this.getBorder().equals(exampleBorder)){ + if (formatExportData != null && !controlPanel.includeExampleExports.isSelected()){ ExportDataRepresentation.SimulationExportDataRepresentation mostRecentTableEntry = !n5ExportTableModel.tableData.isEmpty() ? n5ExportTableModel.tableData.getFirst() : null; Stack jobStack = formatExportData.formatJobIDs; boolean isUpdated = false; @@ -154,7 +133,9 @@ private void automaticRefresh(){ || !formatExportData.simulationDataMap.containsKey(mostRecentTableEntry.jobID))){ break; } - isUpdated = n5ExportTableModel.prependRowData(formatExportData.simulationDataMap.get(currentJob), controlPanel.oldestTimeAllowed()); + isUpdated = n5ExportTableModel.addToRowData(formatExportData.simulationDataMap.get(currentJob), + timeFilter.oldestTimeAllowed(), SearchBar.searchTextField.getText(), + false); } if(isUpdated){ n5ExportTableModel.fireTableDataChanged(); @@ -173,15 +154,16 @@ private void automaticRefresh(){ refreshTableThread.start(); } - public void openSelectedRows(boolean inMemory){ + public void openSelectedRows(boolean openInMemory, boolean performDataReduction, SimResultsLoader.OpenTag openTag){ ArrayList filesToOpen = new ArrayList<>(); for(int row: exportListTable.getSelectedRows()){ String uri = n5ExportTableModel.getRowData(row).uri; ExportDataRepresentation.SimulationExportDataRepresentation rowData = n5ExportTableModel.getRowData(row); - SimResultsLoader simResultsLoader = new SimResultsLoader(uri, rowData.savedFileName, row, rowData.jobID); + SimResultsLoader simResultsLoader = new SimResultsLoader(uri, rowData.savedFileName, row, rowData.jobID, openTag); filesToOpen.add(simResultsLoader); } - N5ImageHandler.loadingFactory.openN5FileDataset(filesToOpen, inMemory); + N5ImageHandler.loadingManager.openN5FileDataset(filesToOpen, openInMemory, + performDataReduction); } public void copySelectedRowLink(){ @@ -213,25 +195,31 @@ public void valueChanged(ListSelectionEvent e) { int row = exportListTable.getSelectedRow(); exportDetailsPanel.resetExportDetails(); if (row > exportListTable.getRowCount() || row < 0){ - controlPanel.enableRowContextDependentButtons(false); + controlPanel.disableAllContextDependentButtons(); return; } - controlPanel.enableRowContextDependentButtons(true); MainPanel.setEnableParentAndChild(exportDetailsPanel, true); ExportDataRepresentation.SimulationExportDataRepresentation rowData = n5ExportTableModel.getRowData(row); exportDetailsPanel.addExportDetailEntries("Variables: " + rowData.variables, rowData.differentParameterValues); int loadingRow = loadingRowsJobID.containsKey(row) ? findLoadingRow(row, row) : -1; - controlPanel.allowCancel(loadingRow != -1); + controlPanel.updateButtonsToMatchState(loadingRow != -1); } - public void removeFromLoadingRows(){ + public void stopSelectedImageFromLoading(){ int row = exportListTable.getSelectedRow(); - N5ImageHandler.loadingFactory.stopOpeningSimulation(n5ExportTableModel.tableData.get(row).jobID); + N5ImageHandler.loadingManager.stopLoadingImage(n5ExportTableModel.tableData.get(row).jobID); loadingRowsJobID.remove(row); exportListTable.repaint(); } + public void removeSpecificRowFromLoadingRows(int rowNumber){ + int realRowNumber = findLoadingRow(rowNumber, rowNumber); + loadingRowsJobID.remove(realRowNumber); + controlPanel.updateButtonsToMatchState(false); + exportListTable.repaint(); + } + @Override public void simIsLoading(int itemRow, String exportID) { loadingRowsJobID.put(itemRow, exportID); @@ -239,13 +227,14 @@ public void simIsLoading(int itemRow, String exportID) { } @Override - public void simFinishedLoading(int itemRow, String exportID) { - loadingRowsJobID.remove(itemRow); - exportListTable.repaint(); - controlPanel.allowCancel(false); + public void simFinishedLoading(SimResultsLoader loadedResults) { + if (loadedResults.openTag == SimResultsLoader.OpenTag.VIEW){ + removeSpecificRowFromLoadingRows(loadedResults.rowNumber); + loadedResults.getImagePlus().show(); + } } - static class N5ExportTableModel extends AbstractTableModel { + public static class N5ExportTableModel extends AbstractTableModel { public final ArrayList headers = new ArrayList(){{ add("BioModel"); add("Application"); @@ -300,23 +289,21 @@ public ExportDataRepresentation.SimulationExportDataRepresentation getRowData(in public void resetData(){ tableData = new LinkedList<>(); } - public boolean appendRowData(ExportDataRepresentation.SimulationExportDataRepresentation rowData, LocalDateTime oldestExportAllowed){ - DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); - LocalDateTime exportDate = LocalDateTime.parse(rowData.exportDate, dateFormat); - if (exportDate.isBefore(oldestExportAllowed)){ - return false; - } - tableData.add(rowData); - return true; + private boolean stringFilter(ExportDataRepresentation.SimulationExportDataRepresentation rowData, String strFilter){ + return rowData.biomodelName.contains(strFilter) || rowData.applicationName.contains(strFilter) || + rowData.simulationName.contains(strFilter) || rowData.savedFileName.contains(strFilter); } - - public boolean prependRowData(ExportDataRepresentation.SimulationExportDataRepresentation rowData, LocalDateTime oldestExportAllowed){ + public boolean addToRowData(ExportDataRepresentation.SimulationExportDataRepresentation rowData, LocalDateTime oldestExportAllowed, String strFilter, boolean append){ DateTimeFormatter dateFormat = DateTimeFormatter.ofPattern("yyyy/MM/dd HH:mm:ss"); LocalDateTime exportDate = LocalDateTime.parse(rowData.exportDate, dateFormat); - if (exportDate.isBefore(oldestExportAllowed)){ + if (exportDate.isBefore(oldestExportAllowed) || !stringFilter(rowData, strFilter)){ return false; } - tableData.addFirst(rowData); + if (append){ + tableData.add(rowData); + } else { + tableData.addFirst(rowData); + } return true; } diff --git a/view-simulation-results/src/main/java/org/vcell/N5/UI/RangeSelector.java b/view-simulation-results/src/main/java/org/vcell/N5/UI/RangeSelector.java index feb47c8..8605a70 100644 --- a/view-simulation-results/src/main/java/org/vcell/N5/UI/RangeSelector.java +++ b/view-simulation-results/src/main/java/org/vcell/N5/UI/RangeSelector.java @@ -1,15 +1,10 @@ package org.vcell.N5.UI; -import org.scijava.log.Logger; -import org.vcell.N5.N5ImageHandler; - import javax.swing.*; import javax.swing.event.EventListenerList; import java.awt.*; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; -import java.awt.event.FocusAdapter; -import java.awt.event.FocusEvent; public class RangeSelector extends JDialog implements ActionListener { public int startC; @@ -19,35 +14,39 @@ public class RangeSelector extends JDialog implements ActionListener { public int startZ; public int endZ; - private final JTextField channelStartTextField; - private final JTextField channelEndTextField; - private final JTextField timeStartTextField; - private final JTextField timeEndTextField; - private final JTextField zStartTextField; - private final JTextField zEndTextField; + private JTextField channelStartTextField; + private JTextField channelEndTextField; + private JTextField timeStartTextField; + private JTextField timeEndTextField; + private JTextField zStartTextField; + private JTextField zEndTextField; public boolean cancel; private static final String okayButtonText = "Okay"; private static final String cancelButtonText = "Cancel"; - public final JButton okayButton; - public final JButton cancelButton; - private final JFrame frame; + public JButton okayButton; + public JButton cancelButton; + private JFrame frame; - private static final Logger logger = N5ImageHandler.getLogger(RangeSelector.class); private static final EventListenerList eventListenerList = new EventListenerList(); private final ControlButtonsPanel controlButtonsPanel = MainPanel.controlButtonsPanel; - public RangeSelector(double cDim, double zDim, double tDim, String userSetFileName){ - channelStartTextField = new HintTextField("1"); + public RangeSelector(){ + + } + + public void displayRangeMenu(double cDim, double zDim, double tDim){ channelEndTextField = new HintTextField("" + (int) cDim); + zEndTextField = new HintTextField("" + (int) zDim); + timeEndTextField = new HintTextField("" + (int) tDim); + String userSetFileName = "Range for All Images Used"; + channelStartTextField = new HintTextField("1"); zStartTextField = new HintTextField("1"); - zEndTextField = new HintTextField("" + (int) zDim); timeStartTextField = new HintTextField("1"); - timeEndTextField = new HintTextField("" + (int) tDim); // Create the frame frame = new JFrame("Select " + userSetFileName + " Dimensions"); @@ -93,15 +92,12 @@ public RangeSelector(double cDim, double zDim, double tDim, String userSetFileNa frame.add(panel); this.setContentPane(panel); this.setModal(true); - } - - public void displayRangeMenu(){ // Make the window visible this.setVisible(true); } public static void main(String[] args) { - RangeSelector inMemoryPopUp = new RangeSelector(1, 2, 3, null); + RangeSelector inMemoryPopUp = new RangeSelector(); } @Override @@ -122,50 +118,9 @@ public void actionPerformed(ActionEvent e) { else if (e.getSource().equals(cancelButton)) { MainPanel.changeCursor(new Cursor(Cursor.DEFAULT_CURSOR)); - controlButtonsPanel.enableCriticalButtons(true); + controlButtonsPanel.enableAllButtons(true); cancel = true; this.setVisible(false); } } - - static class HintTextField extends JTextField { - - Font gainFont = new Font("Tahoma", Font.PLAIN, 11); - Font lostFont = new Font("Tahoma", Font.ITALIC, 11); - - public HintTextField(final String hint) { - - setText(hint); - setFont(lostFont); - setForeground(Color.GRAY); - - this.addFocusListener(new FocusAdapter() { - - @Override - public void focusGained(FocusEvent e) { - if (getText().equals(hint)) { - setText(""); - setFont(gainFont); - } else { - setText(getText()); - setFont(gainFont); - } - } - - @Override - public void focusLost(FocusEvent e) { - if (getText().equals(hint)|| getText().isEmpty()) { - setText(hint); - setFont(lostFont); - setForeground(Color.GRAY); - } else { - setText(getText()); - setFont(gainFont); - setForeground(Color.BLACK); - } - } - }); - - } - } } diff --git a/view-simulation-results/src/main/java/org/vcell/N5/UI/RemoteFileSelection.java b/view-simulation-results/src/main/java/org/vcell/N5/UI/RemoteFileSelection.java index 6f0f827..eadf2bd 100644 --- a/view-simulation-results/src/main/java/org/vcell/N5/UI/RemoteFileSelection.java +++ b/view-simulation-results/src/main/java/org/vcell/N5/UI/RemoteFileSelection.java @@ -95,8 +95,10 @@ public String getS3URL(){ @Override public void actionPerformed(ActionEvent e) { - SimResultsLoader simResultsLoader = new SimResultsLoader(getS3URL(), "", -1, ""); - N5ImageHandler.loadingFactory.openN5FileDataset(new ArrayList(){{add(simResultsLoader);}}, false); + SimResultsLoader simResultsLoader = new SimResultsLoader(getS3URL(), "", -1, "", SimResultsLoader.OpenTag.VIEW); + N5ImageHandler.loadingManager.openN5FileDataset(new ArrayList(){{add(simResultsLoader);}}, + false, + false); this.setVisible(false); } } diff --git a/view-simulation-results/src/main/java/org/vcell/N5/reduction/DTO/RangeOfImage.java b/view-simulation-results/src/main/java/org/vcell/N5/reduction/DTO/RangeOfImage.java new file mode 100644 index 0000000..fe23c90 --- /dev/null +++ b/view-simulation-results/src/main/java/org/vcell/N5/reduction/DTO/RangeOfImage.java @@ -0,0 +1,45 @@ +package org.vcell.N5.reduction.DTO; + +public class RangeOfImage { + public final int timeStart; + public final int timeEnd; + public final int zStart; + public final int zEnd; + public final int channelStart; + public final int channelEnd; + + public RangeOfImage(int timeStart, int timeEnd, int zStart, int zEnd, int channelStart, int channelEnd) { + this.timeStart = timeStart; + this.timeEnd = timeEnd; + this.zStart = zStart; + this.zEnd = zEnd; + this.channelStart = channelStart; + this.channelEnd = channelEnd; + } + + public int getNChannels(){ + return this.channelEnd - this.channelStart + 1; + } + + public int getNFrames(){ + return this.timeEnd - this.timeStart + 1; + } + + public int getNSlices(){ + return this.zEnd - this.zStart + 1; + } + + /** + * Only regards time for range, and is used for normalization. + * @param timeStart + * @param timeEnd + */ + public RangeOfImage(int timeStart, int timeEnd){ + this.timeStart = timeStart; + this.timeEnd = timeEnd; + this.zStart = Integer.MIN_VALUE; + this.zEnd = Integer.MIN_VALUE; + this.channelStart = Integer.MIN_VALUE; + this.channelEnd = Integer.MIN_VALUE; + } +} diff --git a/view-simulation-results/src/main/java/org/vcell/N5/reduction/DTO/ReducedData.java b/view-simulation-results/src/main/java/org/vcell/N5/reduction/DTO/ReducedData.java new file mode 100644 index 0000000..2984c9f --- /dev/null +++ b/view-simulation-results/src/main/java/org/vcell/N5/reduction/DTO/ReducedData.java @@ -0,0 +1,47 @@ +package org.vcell.N5.reduction.DTO; + +import org.vcell.N5.reduction.GUI.SelectMeasurements; + +import java.util.ArrayList; +import java.util.HashMap; + +// Per Image, contain all ROI data +// Shape: [time * z][channel * roi] +public class ReducedData { + private final int nChannels; + private final int nSlices; + private final HashMap dataMap = new HashMap<>(); + + public final ArrayList measurements; + public final RangeOfImage rangeOfImage; + public final int nROIs; + public final HashMap roiNames = new HashMap<>(); + public final HashMap channelNames = new HashMap<>(); + public final String imageName; + public ReducedData(String imageName, RangeOfImage rangeOfImage, int nROIs, ArrayList measurements){ + int nFrames = rangeOfImage.timeEnd - rangeOfImage.timeStart + 1; + nSlices = rangeOfImage.zEnd - rangeOfImage.zStart + 1; + this.measurements = measurements; + this.nChannels = rangeOfImage.getNChannels(); + this.nROIs = nROIs; + this.rangeOfImage = rangeOfImage; + this.imageName = imageName; + + for (SelectMeasurements.AvailableMeasurements measurement: measurements){ + dataMap.put(measurement, new double[nFrames * nSlices][nChannels * nROIs]); + } + } + + public void putDataPoint(double data,int time, int z, int channel, int roi, SelectMeasurements.AvailableMeasurements measurementType){ + dataMap.get(measurementType)[(time * nSlices) + z][(roi * nChannels) + channel] = data; + } + + public double getDataPoint(int time, int z, int channel, int roi, SelectMeasurements.AvailableMeasurements measurementType){ + return dataMap.get(measurementType)[(time * nSlices) + z][(roi * nChannels) + channel]; + } + + public String getWideTableHeader(int r, int c){ + return imageName + ":" + roiNames.get(r) + ":" + channelNames.get(c); + } + +} diff --git a/view-simulation-results/src/main/java/org/vcell/N5/reduction/DataReductionManager.java b/view-simulation-results/src/main/java/org/vcell/N5/reduction/DataReductionManager.java new file mode 100644 index 0000000..1bce13a --- /dev/null +++ b/view-simulation-results/src/main/java/org/vcell/N5/reduction/DataReductionManager.java @@ -0,0 +1,204 @@ +package org.vcell.N5.reduction; + +import com.google.gson.internal.LinkedTreeMap; +import ij.ImagePlus; +import ij.WindowManager; +import ij.gui.Roi; +import org.jetbrains.annotations.NotNull; +import org.vcell.N5.N5ImageHandler; +import org.vcell.N5.UI.ControlButtonsPanel; +import org.vcell.N5.UI.MainPanel; +import org.vcell.N5.reduction.DTO.RangeOfImage; +import org.vcell.N5.reduction.DTO.ReducedData; +import org.vcell.N5.reduction.GUI.DataReductionGUI; +import org.vcell.N5.retrieving.SimLoadingListener; +import org.vcell.N5.retrieving.SimResultsLoader; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.atomic.AtomicBoolean; + +public class DataReductionManager implements SimLoadingListener { + private final ArrayList arrayOfSimRois; + private final Object csvMatrixLock = new Object(); + + private int numOfImagesToOpen; + private final ReductionCalculations calculations; + + public final DataReductionGUI.DataReductionSubmission submission; + + private final ConcurrentHashMap threadPool = new ConcurrentHashMap<>(); + private final Object threadPoolLock = new Object(); + + private DataReductionWriter dataReductionWriter; + + public DataReductionManager(DataReductionGUI.DataReductionSubmission submission){ + N5ImageHandler.loadingManager.addSimLoadingListener(this); + this.submission = submission; + + this.arrayOfSimRois = submission.arrayOfSimRois; + this.numOfImagesToOpen = submission.numOfSimImages + 1; // Plus one for the lab image + this.calculations = new ReductionCalculations(submission.normalizeMeasurementsBool); + + Thread processLabResults = new Thread(() -> { + if (submission.arrayOfLabRois.isEmpty()){ + int height = submission.labResults.getHeight(); + int width = submission.labResults.getWidth(); + Roi roi = new Roi(0, 0, width, height); + roi.setName("Entire Image"); + submission.arrayOfLabRois.add(roi); + } + calculateAndAddResults(submission.labResults, submission.experiementNormRange, submission.experimentImageRange, + submission.arrayOfLabRois, null, "Lab"); + synchronized (threadPoolLock){ + threadPool.remove("Lab"); + } + }, "Processing Lab Image"); + ThreadStruct threadStruct = new ThreadStruct(submission.labResults, new AtomicBoolean(true), processLabResults); + synchronized (threadPoolLock){ + threadPool.put("Lab", threadStruct); + } + } + + //////////////////////// + // General Functions // + ////////////////////// + + private void calculateAndAddResults(ImagePlus imagePlus, RangeOfImage normRange, + RangeOfImage imageRange, ArrayList rois, + LinkedTreeMap> channelInfo, + String threadName){ + HashMap normValue = null; + if (submission.normalizeMeasurementsBool){ + normValue = calculations.calculateNormalValue(imagePlus, normRange, rois, imageRange); + } + + ReducedData reducedData = new ReducedData(imagePlus.getTitle(), imageRange, arrayOfSimRois.size(), submission.selectedMeasurements); + calculations.addAppropriateHeaders(rois, imageRange, reducedData, channelInfo); + + AtomicBoolean continueOperation = threadPool.get(threadName).continueOperation; + calculations.calculateStatistics(imagePlus, rois, normValue, reducedData, imageRange, continueOperation); + if (continueOperation.get()){ + synchronized (csvMatrixLock){ + try { + dataReductionWriter.consumeNewData(reducedData); + } catch (IOException e) { + stopAllThreads(); + throw new RuntimeException(e); + } + numOfImagesToOpen -= 1; + } + } + if (numOfImagesToOpen == 0 && continueOperation.get()){ + try{ + dataReductionWriter.close(); + } catch (IOException ioException){ + throw new RuntimeException(ioException); + } finally { + N5ImageHandler.loadingManager.removeFromSimLoadingListener(this); + MainPanel.controlButtonsPanel.updateButtonsToMatchState(false, ControlButtonsPanel.PanelState.NOTHING_OR_LOADING_IMAGE); + } + } + } + + public void stopAllThreads(){ + synchronized (threadPoolLock){ + for (String threadName: threadPool.keySet()){ + ThreadStruct threadStruct = threadPool.get(threadName); + threadStruct.continueOperation.set(false); + // Experiment image is in thread pool, so trying to retrieve a results loader for it would not work + if (threadStruct.simResultsLoader != null){ + SimResultsLoader loadedResults = threadStruct.simResultsLoader; + MainPanel.n5ExportTable.removeSpecificRowFromLoadingRows(loadedResults.rowNumber); + ImagePlus openImage = WindowManager.getImage(loadedResults.getImagePlus().getID()); + if (openImage != null){ + openImage.close(); + } + } + threadPool.remove(threadName); + } + } + N5ImageHandler.loadingManager.removeFromSimLoadingListener(this); + MainPanel.controlButtonsPanel.updateButtonsToMatchState(false, ControlButtonsPanel.PanelState.NOTHING_OR_LOADING_IMAGE); + } + + + @Override + public void simIsLoading(int itemRow, String exportID) { + + } + + @Override + public void simFinishedLoading(SimResultsLoader loadedResults) { + if (loadedResults.openTag == SimResultsLoader.OpenTag.DATA_REDUCTION){ + ThreadStruct threadStruct = getThreadStruct(loadedResults); + synchronized (threadPoolLock){ + threadPool.put(loadedResults.exportID, threadStruct); + if (threadPool.size() == (submission.numOfSimImages + 1)){ + int maxZ = 0; + int maxT = 0; + // + if (arrayOfSimRois.isEmpty() && !threadStruct.thread.getName().equals("Lab")){ + int height = loadedResults.getImagePlus().getHeight(); + int width = loadedResults.getImagePlus().getWidth(); + Roi roi = new Roi(0, 0, width, height); + roi.setName("Entire Image"); + arrayOfSimRois.add(roi); + } + for (String threadName : threadPool.keySet()){ + int curZ = threadPool.get(threadName).imagePlus.getNSlices(); + int curT = threadPool.get(threadName).imagePlus.getNFrames(); + if (!threadName.equals("Lab") && submission.simImageRange.zEnd - submission.simImageRange.zStart == 0){ + curZ = 1; + } + maxZ = Math.max(curZ, maxZ); + maxT = Math.max(curT, maxT); + } + dataReductionWriter = new DataReductionWriter(submission, maxT, maxZ); + dataReductionWriter.initializeDataSheets(); + for (String threadName : threadPool.keySet()){ + threadPool.get(threadName).thread.start(); + } + } + } + } + } + + private @NotNull ThreadStruct getThreadStruct(SimResultsLoader loadedResults) { + Thread imageProcessingThread = new Thread(() -> { + ImagePlus imagePlus = loadedResults.getImagePlus(); + imagePlus.show(); + dataReductionWriter.addMetaData(loadedResults); + calculateAndAddResults(imagePlus, submission.simNormRange, submission.simImageRange, + submission.arrayOfSimRois, loadedResults.getChannelInfo(), loadedResults.exportID); + MainPanel.n5ExportTable.removeSpecificRowFromLoadingRows(loadedResults.rowNumber); + imagePlus.close(); + synchronized (threadPoolLock){ + threadPool.remove(loadedResults.exportID); + } + }, "Processing Image: " + loadedResults.userSetFileName); + return new ThreadStruct(loadedResults, new AtomicBoolean(true), imageProcessingThread); + } + + static class ThreadStruct { + public final SimResultsLoader simResultsLoader; + public final ImagePlus imagePlus; + public final AtomicBoolean continueOperation; + public final Thread thread; + public ThreadStruct(SimResultsLoader simResultsLoader, AtomicBoolean continueOperation, Thread thread){ + this.simResultsLoader = simResultsLoader; + this.continueOperation = continueOperation; + this.thread = thread; + this.imagePlus = simResultsLoader.getImagePlus(); + } + + public ThreadStruct(ImagePlus imagePlus, AtomicBoolean continueOperation, Thread thread){ + this.imagePlus = imagePlus; + this.simResultsLoader = null; + this.continueOperation = continueOperation; + this.thread = thread; + } + } +} diff --git a/view-simulation-results/src/main/java/org/vcell/N5/reduction/DataReductionWriter.java b/view-simulation-results/src/main/java/org/vcell/N5/reduction/DataReductionWriter.java new file mode 100644 index 0000000..65c5509 --- /dev/null +++ b/view-simulation-results/src/main/java/org/vcell/N5/reduction/DataReductionWriter.java @@ -0,0 +1,268 @@ +package org.vcell.N5.reduction; + +import com.opencsv.CSVWriter; +import org.vcell.N5.ExportDataRepresentation; +import org.vcell.N5.UI.MainPanel; +import org.vcell.N5.UI.N5ExportTable; +import org.vcell.N5.reduction.DTO.RangeOfImage; +import org.vcell.N5.reduction.DTO.ReducedData; +import org.vcell.N5.reduction.GUI.DataReductionGUI; +import org.vcell.N5.reduction.GUI.SelectMeasurements; +import org.vcell.N5.retrieving.SimResultsLoader; + +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; + +public class DataReductionWriter{ + private final Object metaDataLock = new Object(); + private final File file; + + public final DataReductionGUI.DataReductionSubmission submission; + + private final ArrayList> averageMatrix = new ArrayList<>(); + private final ArrayList> standardDivMatrix = new ArrayList<>(); + private final ArrayList> maxIntensityMatrix = new ArrayList<>(); + private final ArrayList> minIntensityMatrix = new ArrayList<>(); + private final ArrayList> metaDataSheet = new ArrayList<>(); + + + private final HashMap>> sheetsAvailable = new HashMap>>(){{ + put(SelectMeasurements.AvailableMeasurements.AVERAGE, averageMatrix); + put(SelectMeasurements.AvailableMeasurements.STD_DEV, standardDivMatrix); + put(SelectMeasurements.AvailableMeasurements.MAX_INTENSITY, maxIntensityMatrix); + put(SelectMeasurements.AvailableMeasurements.MIN_INTENSITY, minIntensityMatrix); + }}; + private final HashMap columnsForSheets = new HashMap<>(); + + private int metaDataParameterCol = 5; + private final HashMap parameterNameToCol = new HashMap<>(); + + private final ArrayList selectedMeasurements; + + private final int maxZ; + private final int maxT; + + private final boolean wideTable; + + /////////////////////////////////////// + // Initialize Sheet and Lab results // + ///////////////////////////////////// + + public DataReductionWriter(DataReductionGUI.DataReductionSubmission submission, int maxT, int maxZ){ + this.submission = submission; + this.selectedMeasurements = submission.selectedMeasurements; + this.file = submission.fileToSaveResultsTo; + this.maxZ = maxZ; + this.maxT = maxT; + this.wideTable = submission.wideTable; + } + + public void consumeNewData(ReducedData reducedData) throws IOException { + if (wideTable){ + addValuesToWideCSVMatrix(reducedData); + } else { + appendAndWriteTallTable(reducedData); + } + } + + public void close() throws IOException { + if (wideTable){ + writeWideTableCSVMatrix(); + } + writeMetaDataTable(); + } + + public void initializeDataSheets(){ + ArrayList headers = new ArrayList(){{add("Time Frame");}}; + boolean is3D = maxZ > 1; + + // Add Time and Z-Index Columns + + + metaDataSheet.add(new ArrayList<>()); + metaDataSheet.get(0).add(""); + metaDataSheet.get(0).add("BioModel Name"); + metaDataSheet.get(0).add("Application Name"); + metaDataSheet.get(0).add("Simulation Name"); + metaDataSheet.get(0).add("N5 URL"); + + if (wideTable){ + // Fill in Time and Z-Index Columns with selected range + if (is3D){ + headers.add("Z Index"); + } + for (SelectMeasurements.AvailableMeasurements measurement : selectedMeasurements){ + ArrayList> dataSheet = sheetsAvailable.get(measurement); + dataSheet.add(new ArrayList<>()); + ArrayList headerRow = dataSheet.get(0); + + for (int i = 0; i < headers.size(); i++){ + headerRow.add(i, headers.get(i)); + } + columnsForSheets.put(measurement, headers.size() + 1); + } + + for (SelectMeasurements.AvailableMeasurements measurement : selectedMeasurements){ + ArrayList> dataSheet = sheetsAvailable.get(measurement); + for (int t = 1; t <= maxT; t++){ + for (int z = 1; z <= maxZ; z++){ + ArrayList pointRow = new ArrayList<>(); + pointRow.add(0, String.valueOf(t)); + if (is3D){ + pointRow.add(1, String.valueOf(z)); + } + dataSheet.add(pointRow); + } + } + } + } else { + headers.add(1, "Z Index"); + headers.add("Image Name"); + headers.add("ROI Name"); + headers.add("Channel Name"); + for (SelectMeasurements.AvailableMeasurements measurement : selectedMeasurements){ + headers.add(measurement.publicName); + } + File file = new File(this.file.getAbsolutePath() + ".csv"); + try(FileWriter fileWriter = new FileWriter(file)) { + CSVWriter csvWriter = new CSVWriter(fileWriter); + csvWriter.writeNext(headers.toArray(new String[0])); + } catch (IOException ioException){ + throw new RuntimeException("Can't write to CSV file.", ioException); + } + } + } + + // If parameter is not in list of parameters, add new column. If simulation does not have parameter say "not-present" + public void addMetaData(SimResultsLoader loadedResults){ + synchronized (metaDataLock){ + N5ExportTable n5ExportTable = MainPanel.n5ExportTable; + ExportDataRepresentation.SimulationExportDataRepresentation data = n5ExportTable.n5ExportTableModel.getRowData(loadedResults.rowNumber); + ArrayList newMetaData = new ArrayList<>(); + newMetaData.add(loadedResults.userSetFileName); + newMetaData.add(data.biomodelName); + newMetaData.add(data.applicationName); + newMetaData.add(data.simulationName); + newMetaData.add(data.uri); + ArrayList parameterValues = data.differentParameterValues; + for (String s : parameterValues){ + String[] tokens = s.split(":"); + String colValue = tokens[1] + ":" + tokens[2]; + if (parameterNameToCol.containsKey(tokens[0])){ + int col = parameterNameToCol.get(tokens[0]); + fillWithEmptySpace(newMetaData, col); + newMetaData.add(col, colValue); + } else{ + metaDataSheet.get(0).add(tokens[0] + " (Default:Set Value)"); + fillWithEmptySpace(newMetaData, metaDataParameterCol); + newMetaData.add(metaDataParameterCol, colValue); + parameterNameToCol.put(tokens[0], metaDataParameterCol); + metaDataParameterCol += 1; + } + } + metaDataSheet.add(newMetaData); + } + } + + //////////////////////// + // Private Functions // + ////////////////////// + + private void addValuesToWideCSVMatrix(ReducedData reducedData){ + for (SelectMeasurements.AvailableMeasurements measurement: reducedData.measurements){ + ArrayList> dataSheet = sheetsAvailable.get(measurement); + int colIndex = columnsForSheets.get(measurement); + fillWithEmptySpace(dataSheet.get(0), colIndex); + RangeOfImage rangeOfImage = reducedData.rangeOfImage; + for (int c = 0; c < rangeOfImage.getNChannels(); c++){ + for (int r = 0; r < reducedData.nROIs; r++){ + dataSheet.get(0).add(colIndex, reducedData.getWideTableHeader(r, c)); + int tzCounter = 1; + for (int t = 1; t <= maxT; t++){ + for (int z = 1; z <= maxZ; z++){ + boolean inBetweenTime = t <= rangeOfImage.timeEnd && rangeOfImage.timeStart <= t; + boolean onlyOneZ = rangeOfImage.zEnd - rangeOfImage.zStart == 0 && maxZ == 1; + boolean inBetweenZ = z <= rangeOfImage.zEnd && rangeOfImage.zStart <= z || onlyOneZ; + ArrayList row = dataSheet.get(tzCounter); + fillWithEmptySpace(row, colIndex); + if (inBetweenTime && inBetweenZ){ + int nt = t - rangeOfImage.timeStart; + int nz = onlyOneZ ? 0 : z - rangeOfImage.zStart; + row.add(String.valueOf(reducedData.getDataPoint(nt, nz, c, r, measurement))); + } + tzCounter += 1; + } + } + colIndex += 1; + } + } + colIndex += 1; + columnsForSheets.replace(measurement, colIndex); + } + } + + // If specific entry to be added isn't in array list length, add empty space until it is + private void fillWithEmptySpace(ArrayList arrayList, int col){ + while (arrayList.size() < col){ + arrayList.add(""); + } + } + + // Each reduced data is a different measurement type + private void appendAndWriteTallTable(ReducedData reducedData) throws IOException { + RangeOfImage rangeOfImage = reducedData.rangeOfImage; + for (int t = rangeOfImage.timeStart; t <= rangeOfImage.timeEnd; t++) { + for (int z = rangeOfImage.zStart; z <= rangeOfImage.zEnd; z++) { + for (int channel = 0; channel < rangeOfImage.getNChannels(); channel++){ + for(int roi= 0; roi < reducedData.nROIs; roi++){ + File file = new File(this.file.getAbsolutePath() + ".csv"); + ArrayList newRow = new ArrayList<>(Arrays.asList(String.valueOf(t), String.valueOf(z), + reducedData.imageName, reducedData.roiNames.get(roi), reducedData.channelNames.get(channel))); + for (SelectMeasurements.AvailableMeasurements measurement : reducedData.measurements){ + int nt = t - rangeOfImage.timeStart; + int nz = z - rangeOfImage.zStart; + newRow.add(String.valueOf(reducedData.getDataPoint(nt, nz, channel, roi, measurement))); + } + try (FileWriter fileWriter = new FileWriter(file, true)) { + CSVWriter csvWriter = new CSVWriter(fileWriter); + csvWriter.writeNext(newRow.toArray(new String[0])); + } + } + } + } + } + } + + private void writeWideTableCSVMatrix() throws IOException { + for (SelectMeasurements.AvailableMeasurements measurements : sheetsAvailable.keySet()){ + if (!sheetsAvailable.get(measurements).isEmpty()){ + File currentFile = new File(file.getAbsolutePath() + "-" + measurements.publicName + ".csv"); + try (FileWriter fileWriter = new FileWriter(currentFile)){ + CSVWriter csvWriter = new CSVWriter(fileWriter); + for (ArrayList row : sheetsAvailable.get(measurements)){ + csvWriter.writeNext(row.toArray(new String[0])); + } + } + } + } + } + + private void writeMetaDataTable() throws IOException { + File currentFile = new File(file.getAbsolutePath() + "-Metadata.csv"); + try (FileWriter fileWriter = new FileWriter(currentFile)){ + CSVWriter csvWriter = new CSVWriter(fileWriter); + for (ArrayList row : metaDataSheet){ + csvWriter.writeNext(row.toArray(new String[0])); + } + } + } +} + + + + + diff --git a/view-simulation-results/src/main/java/org/vcell/N5/reduction/GUI/DataReductionGUI.java b/view-simulation-results/src/main/java/org/vcell/N5/reduction/GUI/DataReductionGUI.java new file mode 100644 index 0000000..7be7adf --- /dev/null +++ b/view-simulation-results/src/main/java/org/vcell/N5/reduction/GUI/DataReductionGUI.java @@ -0,0 +1,231 @@ +package org.vcell.N5.reduction.GUI; + +import ij.ImagePlus; +import ij.WindowManager; +import ij.gui.Roi; +import org.vcell.N5.UI.ControlButtonsPanel; +import org.vcell.N5.UI.MainPanel; +import org.vcell.N5.reduction.DTO.RangeOfImage; +import org.vcell.N5.retrieving.SimResultsLoader; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.EmptyBorder; +import javax.swing.border.EtchedBorder; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.util.ArrayList; + +public class DataReductionGUI extends JPanel implements ActionListener { + private JComboBox chosenImage; + private final JCheckBox selectRangeOfMeasurement = new JCheckBox("Select Measurement Range: "); + private final JCheckBox normalizeMeasurement = new JCheckBox("Normalize Measurement: "); + private final JCheckBox choseCSVTableFormat = new JCheckBox("Choose CSV Format: "); + + private final JDialog jDialog = new JDialog(MainPanel.exportTableDialog, true); + private final JButton okayButton = new JButton("Okay"); + private final JButton cancelButton = new JButton("Cancel"); + private File chosenFile; + + private final ArrayList filesToOpen; + + public int fileChooserReturnValue; + + private final SelectSimRange selectSimRange; + private final RoiSelection roiSelection; + private final NormalizeGUI normalizeGUI; + private final SelectMeasurements selectMeasurements; + private final SelectTableFormat selectTableFormat; + + private boolean continueWithProcess = false; + + private final Border lowerEtchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED); + + public DataReductionGUI(ArrayList filesToOpen, double simCSize, double simZSize, double simTSize){ + this.filesToOpen = filesToOpen; + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + okayButton.setEnabled(false); + + + selectSimRange = new SelectSimRange(jDialog, simCSize, simZSize, simTSize); + roiSelection = new RoiSelection(this); + normalizeGUI = new NormalizeGUI(jDialog, simTSize); + selectMeasurements = new SelectMeasurements(this); + selectTableFormat = new SelectTableFormat(); + + JPanel imagesToMeasurePanel = new JPanel(); + imagesToMeasurePanel.setLayout(new BoxLayout(imagesToMeasurePanel, BoxLayout.Y_AXIS)); + imagesToMeasurePanel.setBorder(BorderFactory.createTitledBorder(lowerEtchedBorder, "Images To Measure")); + imagesToMeasurePanel.add(imageSelectionPanel()); + imagesToMeasurePanel.add(selectedImagesToOpenPanel()); + + add(imagesToMeasurePanel); + add(roiSelection); + add(selectMeasurements); + add(displayOptionsPanel()); + add(selectTableFormat); +// add(normalizeGUI); + add(selectSimRange); + add(okayCancelPanel()); + + setVisible(true); + + okayButton.addActionListener(this); + cancelButton.addActionListener(this); + normalizeMeasurement.addActionListener(this); + selectRangeOfMeasurement.addActionListener(this); + choseCSVTableFormat.addActionListener(this); + + this.setBorder(new EmptyBorder(15, 12, 15, 12)); + + jDialog.add(this); + jDialog.setVisible(true); + jDialog.setResizable(true); + jDialog.setModalityType(Dialog.ModalityType.APPLICATION_MODAL); + jDialog.setTitle("Measurement Script"); + jDialog.pack(); + } + + public DataReductionSubmission createSubmission(){ + int[] simRange = normalizeGUI.getSimTimRange(); + int[] labRange = normalizeGUI.getImageTimeRange(); + return new DataReductionSubmission(normalizeMeasurement.isSelected(), + roiSelection.getSimROIList(), roiSelection.getImageROIList(), + WindowManager.getImage((String) chosenImage.getSelectedItem()), + simRange[0], simRange[1], labRange[0], labRange[1], filesToOpen.size(), chosenFile, + selectSimRange.getRangeOfSim(), selectMeasurements.getChosenMeasurements(), selectTableFormat.isWideTableSelected()); + } + + private JPanel okayCancelPanel(){ + JPanel jPanel = new JPanel(); + jPanel.add(okayButton); + jPanel.add(cancelButton); + return jPanel; + } + + private JPanel imageSelectionPanel(){ + JPanel jPanel = new JPanel(new GridLayout(1, 2)); + jPanel.add(new JLabel("Experimental")); + chosenImage = new JComboBox<>(WindowManager.getImageTitles()); + jPanel.add(chosenImage); + return jPanel; + } + + private JPanel selectedImagesToOpenPanel(){ + JPanel jPanel = new JPanel(); + String[] namesOfImagesToOpen = new String[filesToOpen.size()]; + for (int i = 0; i < filesToOpen.size(); i++){ + namesOfImagesToOpen[i] = filesToOpen.get(i).userSetFileName; + } + JList selectedImagesToOpen = new JList<>(namesOfImagesToOpen); + selectedImagesToOpen.setEnabled(false); + selectedImagesToOpen.setVisibleRowCount(4); + JScrollPane jScrollPane = new JScrollPane(selectedImagesToOpen); + jPanel.add(new JLabel("Selected Simulations")); + jPanel.add(jScrollPane); + jPanel.setLayout(new GridLayout(1, 2)); + return jPanel; + } + + private JPanel displayOptionsPanel(){ + JPanel jPanel = new JPanel(); + jPanel.setLayout(new BoxLayout(jPanel, BoxLayout.X_AXIS)); +// jPanel.add(normalizeMeasurement); + jPanel.add(selectRangeOfMeasurement); + jPanel.add(choseCSVTableFormat); + return jPanel; + } + + public void activateOkayButton(){ + boolean selectedAMeasurement = !selectMeasurements.getChosenMeasurements().isEmpty(); + boolean chosenExperimentImage = chosenImage.getSelectedItem() != null; +// boolean roisIsSelected = !roiSelection.getImageROIList().isEmpty() && !roiSelection.getSimROIList().isEmpty(); + okayButton.setEnabled(selectedAMeasurement && chosenExperimentImage); + } + + @Override + public void actionPerformed(ActionEvent e) { + if (e.getSource().equals(normalizeMeasurement)) { + normalizeGUI.setVisible(normalizeMeasurement.isSelected()); + } else if (e.getSource().equals(selectRangeOfMeasurement)){ + selectSimRange.setVisible(selectRangeOfMeasurement.isSelected()); + } else if (e.getSource().equals(choseCSVTableFormat)) { + selectTableFormat.setVisible(choseCSVTableFormat.isSelected()); + selectTableFormat.revalidate(); + selectTableFormat.repaint(); + jDialog.revalidate(); + jDialog.repaint(); + jDialog.pack(); + } else if (e.getSource().equals(okayButton)) { + JFileChooser saveToFile = new JFileChooser(); + saveToFile.setFileSelectionMode(JFileChooser.FILES_ONLY); + fileChooserReturnValue = saveToFile.showDialog(this, "Save Results To File"); + if (fileChooserReturnValue == JFileChooser.APPROVE_OPTION){ + chosenFile = saveToFile.getSelectedFile(); + MainPanel.controlButtonsPanel.updateButtonsToMatchState(); + continueWithProcess = true; + jDialog.dispose(); + } + } else if (e.getSource().equals(cancelButton)) { + MainPanel.controlButtonsPanel.updateButtonsToMatchState(false, ControlButtonsPanel.PanelState.NOTHING_OR_LOADING_IMAGE); + jDialog.dispose(); + } + } + + public boolean shouldContinueWithProcess() { + return continueWithProcess; + } + + public static class DataReductionSubmission{ + public final boolean normalizeMeasurementsBool; + public final ArrayList arrayOfSimRois; + public final ArrayList arrayOfLabRois; + public final ImagePlus labResults; + public final int numOfSimImages; + public final File fileToSaveResultsTo; + + public final RangeOfImage experiementNormRange; + public final RangeOfImage simNormRange; + + public final RangeOfImage experimentImageRange; + public final RangeOfImage simImageRange; + public final ArrayList selectedMeasurements; + + public final boolean wideTable; + + public DataReductionSubmission(boolean normalizeMeasurementsBool, ArrayList arrayOfSimRois, ArrayList arrayOfLabRois, + ImagePlus labResults, int simStartPointNorm, int simEndPointNorm, int imageStartPointNorm, + int imageEndPointNorm, int numOfSimImages, File fileToSaveResultsTo, + RangeOfImage simRange, ArrayList selectedMeasurements, boolean wideTable){ + this.normalizeMeasurementsBool = normalizeMeasurementsBool; + this.arrayOfLabRois = arrayOfLabRois; + this.arrayOfSimRois = arrayOfSimRois; + this.labResults = labResults; + this.numOfSimImages = numOfSimImages; + this.fileToSaveResultsTo = fileToSaveResultsTo; + + this.experiementNormRange = new RangeOfImage(imageStartPointNorm, imageEndPointNorm); + this.simNormRange = new RangeOfImage(simStartPointNorm, simEndPointNorm); + + this.experimentImageRange = new RangeOfImage(1, labResults.getNFrames(), + 1, labResults.getNSlices(), 1, labResults.getNChannels()); + this.simImageRange = simRange; + this.selectedMeasurements = selectedMeasurements; + this.wideTable = wideTable; + } + } + + public static void main(String[] args) { + DataReductionGUI dataReductionGUI = new DataReductionGUI(new ArrayList<>(), 0, 0, 0); + } +} + + + + + + + + diff --git a/view-simulation-results/src/main/java/org/vcell/N5/reduction/GUI/NormalizeGUI.java b/view-simulation-results/src/main/java/org/vcell/N5/reduction/GUI/NormalizeGUI.java new file mode 100644 index 0000000..4072d8d --- /dev/null +++ b/view-simulation-results/src/main/java/org/vcell/N5/reduction/GUI/NormalizeGUI.java @@ -0,0 +1,75 @@ +package org.vcell.N5.reduction.GUI; + +import org.vcell.N5.UI.HintTextField; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.EtchedBorder; +import java.awt.*; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; + +class NormalizeGUI extends JPanel { + private final JTextField createNormFromImageStart; + private final JTextField createNormFromImageEnd; + private final JTextField createNormFromSimStart; + private final JTextField createNormFromSimEnd; + private final JPanel entireImageFramesJPanel; + + public NormalizeGUI(JDialog jDialog, double simTSize){ + Border lowerEtchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED); + Border normalizeBorder = BorderFactory.createTitledBorder(lowerEtchedBorder, "Timeframe Range to Create Norm"); + + JPanel fromImage = new JPanel(new GridLayout()); + createNormFromImageStart = new JTextField(); + createNormFromImageEnd = new JTextField(); + fromImage.add(new JLabel("Exp. Timeframe: ")); + fromImage.add(createNormFromImageStart); + fromImage.add(new JLabel("to")); + fromImage.add(createNormFromImageEnd); + + JPanel fromSim = new JPanel(new GridLayout()); + createNormFromSimStart = new HintTextField("1"); + createNormFromSimEnd = new HintTextField(String.valueOf((int) simTSize)); + fromSim.add(new JLabel("Sim Timeframe: ")); + fromSim.add(createNormFromSimStart); + fromSim.add(new JLabel("to")); + fromSim.add(createNormFromSimEnd); + + entireImageFramesJPanel = new JPanel(new GridLayout(3, 1)); + entireImageFramesJPanel.add(fromImage); + entireImageFramesJPanel.add(fromSim); + this.addComponentListener(new ComponentAdapter() { + @Override + public void componentShown(ComponentEvent e) { + jDialog.revalidate(); + jDialog.repaint(); + jDialog.pack(); + } + @Override + public void componentHidden(ComponentEvent e) { + jDialog.revalidate(); + jDialog.repaint(); + jDialog.pack(); + } + }); + this.add(entireImageFramesJPanel); + this.setBorder(normalizeBorder); + this.setVisible(false); + this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + } + + public int[] getSimTimRange(){ + return new int[]{ + createNormFromSimStart.getText().isEmpty() ? Integer.MIN_VALUE : Integer.parseInt(createNormFromSimStart.getText()), + createNormFromSimEnd.getText().isEmpty() ? Integer.MIN_VALUE: Integer.parseInt(createNormFromSimEnd.getText()) + }; + } + + public int[] getImageTimeRange(){ + return new int[]{ + createNormFromImageStart.getText().isEmpty() ? Integer.MIN_VALUE: Integer.parseInt(createNormFromImageStart.getText()), + createNormFromImageEnd.getText().isEmpty() ? Integer.MIN_VALUE: Integer.parseInt(createNormFromImageEnd.getText()) + }; + } +} diff --git a/view-simulation-results/src/main/java/org/vcell/N5/reduction/GUI/RoiSelection.java b/view-simulation-results/src/main/java/org/vcell/N5/reduction/GUI/RoiSelection.java new file mode 100644 index 0000000..731ded8 --- /dev/null +++ b/view-simulation-results/src/main/java/org/vcell/N5/reduction/GUI/RoiSelection.java @@ -0,0 +1,120 @@ +package org.vcell.N5.reduction.GUI; + +import ij.gui.Roi; +import ij.io.RoiDecoder; + +import javax.swing.*; +import java.awt.*; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.util.ArrayList; + +class RoiSelection extends JPanel { + private final ROIDataModel imageTableModel = new ROIDataModel(); + private final ROIDataModel simTableModel = new ROIDataModel(); + private final DataReductionGUI parentGUI; + + public RoiSelection(DataReductionGUI parentGUI){ + this.setLayout(new BoxLayout(this, BoxLayout.X_AXIS)); + + JList imageROITable = new JList<>(imageTableModel); + JFileChooser imageROIFileChooser = new JFileChooser(); + this.add(createROIInput(imageROITable, imageTableModel, imageROIFileChooser, "Experimental")); + + JList simROITable = new JList<>(simTableModel); + JFileChooser simROIFileChooser = new JFileChooser(); + this.add(createROIInput(simROITable, simTableModel, simROIFileChooser, "Sim")); + this.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Apply 2D ROI Files")); + this.parentGUI = parentGUI; + } + + private JPanel createROIInput(JList jList, ROIDataModel roiDataModel, + JFileChooser fileChooser, String label){ + JPanel jPanel = new JPanel(); + jPanel.setLayout(new BoxLayout(jPanel, BoxLayout.Y_AXIS)); + jList.setVisibleRowCount(4); + JScrollPane jScrollPane = new JScrollPane(jList); + jPanel.add(jScrollPane); + + JButton addButton = new JButton("+"); + addButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + ArrayList roiList = fillROIList(fileChooser); + for (Roi roi : roiList){ + roiDataModel.addRow(roi); + } + parentGUI.activateOkayButton(); + jList.updateUI(); + } + }); + JButton minusButton = new JButton("-"); + minusButton.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + for (int roiIndex: jList.getSelectedIndices()){ + roiDataModel.removeRow(roiIndex); + } + parentGUI.activateOkayButton(); + jList.updateUI(); + } + }); + JPanel buttonPanel = new JPanel(new GridLayout(1, 2)); + buttonPanel.add(addButton); + buttonPanel.add(minusButton); + jPanel.add(buttonPanel); + jPanel.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEmptyBorder(), label)); + + return jPanel; + } + + public ArrayList getImageROIList(){ + return imageTableModel.data; + } + + public ArrayList getSimROIList(){ + return simTableModel.data; + } + + private void setROIPanelSettings(JPanel jPanel, JButton button, JScrollPane jScrollPane){ + GridBagConstraints gridBagConstraints = new GridBagConstraints(); + gridBagConstraints.gridy = 0; + gridBagConstraints.gridx = 0; + gridBagConstraints.fill = GridBagConstraints.BOTH; + gridBagConstraints.insets = new Insets(5, 5, 5, 5); + jPanel.add(button, gridBagConstraints); + gridBagConstraints.gridy = 1; + jPanel.add(jScrollPane, gridBagConstraints); + } + + private ArrayList fillROIList(JFileChooser fileChooser){ + fileChooser.setMultiSelectionEnabled(true); + int choice = fileChooser.showDialog(this, "Open ROI's"); + ArrayList roiList = new ArrayList<>(); + if (choice == JFileChooser.APPROVE_OPTION){ + for (File file: fileChooser.getSelectedFiles()){ + roiList.add(RoiDecoder.open(file.getAbsolutePath())); + } + } + return roiList; + } + + static class ROIDataModel extends AbstractListModel { + private final ArrayList data = new ArrayList<>(); + public void addRow(Roi roi){ + data.add(roi); + } + public void removeRow(int index){data.remove(index);} + + @Override + public int getSize() { + return data.size(); + } + + @Override + public String getElementAt(int index) { + return data.get(index).getName(); + } + } +} diff --git a/view-simulation-results/src/main/java/org/vcell/N5/reduction/GUI/SelectMeasurements.java b/view-simulation-results/src/main/java/org/vcell/N5/reduction/GUI/SelectMeasurements.java new file mode 100644 index 0000000..782ca42 --- /dev/null +++ b/view-simulation-results/src/main/java/org/vcell/N5/reduction/GUI/SelectMeasurements.java @@ -0,0 +1,71 @@ +package org.vcell.N5.reduction.GUI; + +import javax.swing.*; +import javax.swing.event.ListSelectionEvent; +import javax.swing.event.ListSelectionListener; +import java.awt.*; +import java.util.ArrayList; + +public class SelectMeasurements extends JPanel implements ListSelectionListener { + private final JList chosenMeasurement; + private final MeasurementsDataModel measurementsDataModel = new MeasurementsDataModel(); + private final DataReductionGUI parentGUI; + + public SelectMeasurements(DataReductionGUI parentGUI){ + setLayout(new GridLayout(1, 1)); + this.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(), "Measurement Type")); + chosenMeasurement = new JList<>(measurementsDataModel); + chosenMeasurement.setVisibleRowCount(3); + chosenMeasurement.addListSelectionListener(this); + JScrollPane jScrollPane = new JScrollPane(chosenMeasurement); + this.add(jScrollPane); + this.parentGUI = parentGUI; + } + + public ArrayList getChosenMeasurements(){ + return measurementsDataModel.getSelectedMeasurements(chosenMeasurement.getSelectedIndices()); + } + + @Override + public void valueChanged(ListSelectionEvent e) { + if (e.getSource().equals(chosenMeasurement)){ + parentGUI.activateOkayButton(); + } + } + + public enum AvailableMeasurements{ + AVERAGE("Average"), + STD_DEV("Standard Deviation"), + MAX_INTENSITY("Max Intensity"), + MIN_INTENSITY("Minimum Intensity"); + + public final String publicName; + AvailableMeasurements(String publicName){ + this.publicName = publicName; + } + } + + static class MeasurementsDataModel extends AbstractListModel{ + private final AvailableMeasurements[] availableMeasurements = new AvailableMeasurements[]{ + AvailableMeasurements.AVERAGE, AvailableMeasurements.STD_DEV, AvailableMeasurements.MAX_INTENSITY, AvailableMeasurements.MIN_INTENSITY}; + + public ArrayList getSelectedMeasurements(int[] indices){ + ArrayList selected = new ArrayList<>(); + for (int i : indices){ + selected.add(availableMeasurements[i]); + } + return selected; + } + + @Override + public int getSize() { + return availableMeasurements.length; + } + + @Override + public String getElementAt(int index) { + return availableMeasurements[index].publicName; + } + } + +} diff --git a/view-simulation-results/src/main/java/org/vcell/N5/reduction/GUI/SelectSimRange.java b/view-simulation-results/src/main/java/org/vcell/N5/reduction/GUI/SelectSimRange.java new file mode 100644 index 0000000..600b57c --- /dev/null +++ b/view-simulation-results/src/main/java/org/vcell/N5/reduction/GUI/SelectSimRange.java @@ -0,0 +1,85 @@ +package org.vcell.N5.reduction.GUI; + +import org.vcell.N5.UI.HintTextField; +import org.vcell.N5.reduction.DTO.RangeOfImage; + +import javax.swing.*; +import javax.swing.border.Border; +import javax.swing.border.EtchedBorder; +import java.awt.*; +import java.awt.event.ComponentAdapter; +import java.awt.event.ComponentEvent; + +class SelectSimRange extends JPanel { + private final JTextField cStart = new HintTextField("1"); + private final JTextField cEnd; + + private final JTextField zStart = new HintTextField("1"); + private final JTextField zEnd; + + private final JTextField tStart = new HintTextField("1"); + private final JTextField tEnd; + + public SelectSimRange(JDialog jDialog, double simCSize, double simZSize, double simTSize){ + Border lowerEtchedBorder = BorderFactory.createEtchedBorder(EtchedBorder.LOWERED); + Border rangeBorder = BorderFactory.createTitledBorder(lowerEtchedBorder, "Range of Simulation to Perform Measurement"); + cEnd = new HintTextField(String.valueOf((int) simCSize - 1)); + zEnd = new HintTextField(String.valueOf((int) simZSize)); + tEnd = new HintTextField(String.valueOf((int) simTSize)); + + JPanel cRange = new JPanel(new GridLayout()); + cRange.add(new JLabel("Channel Range: ")); + cRange.add(cStart); + cRange.add(new JLabel("to")); + cRange.add(cEnd); + + JPanel zRange = new JPanel(new GridLayout()); + zRange.add(new JLabel("Z Range: ")); + zRange.add(zStart); + zRange.add(new JLabel("to")); + zRange.add(zEnd); + + JPanel tRange = new JPanel(new GridLayout()); + tRange.add(new JLabel("Time Frame's: ")); + tRange.add(tStart); + tRange.add(new JLabel("to")); + tRange.add(tEnd); + + JPanel rangeSelectionPanel = new JPanel(new GridLayout(4, 1)); + rangeSelectionPanel.add(cRange); + rangeSelectionPanel.add(zRange); + rangeSelectionPanel.add(tRange); + + this.addComponentListener(new ComponentAdapter() { + @Override + public void componentShown(ComponentEvent e) { + SelectSimRange.this.revalidate(); + SelectSimRange.this.repaint(); + jDialog.revalidate(); + jDialog.repaint(); + jDialog.pack(); + } + @Override + public void componentHidden(ComponentEvent e) { + SelectSimRange.this.revalidate(); + SelectSimRange.this.repaint(); + jDialog.revalidate(); + jDialog.repaint(); + jDialog.pack(); + } + }); + this.add(rangeSelectionPanel); + this.setBorder(rangeBorder); + this.setVisible(false); + this.setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); + } + + public RangeOfImage getRangeOfSim(){ + RangeOfImage rangeOfImage = new RangeOfImage( + Integer.parseInt(tStart.getText()), Integer.parseInt(tEnd.getText()), + Integer.parseInt(zStart.getText()), Integer.parseInt(zEnd.getText()), + Integer.parseInt(cStart.getText()), Integer.parseInt(cEnd.getText()) + ); + return rangeOfImage; + } +} diff --git a/view-simulation-results/src/main/java/org/vcell/N5/reduction/GUI/SelectTableFormat.java b/view-simulation-results/src/main/java/org/vcell/N5/reduction/GUI/SelectTableFormat.java new file mode 100644 index 0000000..e90076a --- /dev/null +++ b/view-simulation-results/src/main/java/org/vcell/N5/reduction/GUI/SelectTableFormat.java @@ -0,0 +1,29 @@ +package org.vcell.N5.reduction.GUI; + +import javax.swing.*; +import javax.swing.border.EtchedBorder; +import java.awt.*; + +public class SelectTableFormat extends JPanel { + private final JRadioButton wideTable = new JRadioButton("Wide (Best for manual analysis)"); + + public SelectTableFormat(){ + this.setLayout(new GridLayout(1,1)); + ButtonGroup buttonGroup = new ButtonGroup(); + buttonGroup.add(wideTable); + JRadioButton tallTable = new JRadioButton("Tall (Best for scripts)"); + buttonGroup.add(tallTable); + this.add(wideTable); + this.add(tallTable); + this.setBorder(BorderFactory.createTitledBorder(BorderFactory.createEtchedBorder(EtchedBorder.LOWERED), "Select CSV Table Format:")); + wideTable.setSelected(true); + this.setVisible(false); + } + + + public boolean isWideTableSelected(){ + return wideTable.isSelected(); + } + + +} diff --git a/view-simulation-results/src/main/java/org/vcell/N5/reduction/ReductionCalculations.java b/view-simulation-results/src/main/java/org/vcell/N5/reduction/ReductionCalculations.java new file mode 100644 index 0000000..8580906 --- /dev/null +++ b/view-simulation-results/src/main/java/org/vcell/N5/reduction/ReductionCalculations.java @@ -0,0 +1,113 @@ +package org.vcell.N5.reduction; + +import com.google.gson.internal.LinkedTreeMap; +import ij.ImagePlus; +import ij.gui.Roi; +import org.vcell.N5.reduction.DTO.RangeOfImage; +import org.vcell.N5.reduction.DTO.ReducedData; +import org.vcell.N5.reduction.GUI.SelectMeasurements; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.concurrent.atomic.AtomicBoolean; + + +class ReductionCalculations { + private final boolean normalize; + + public ReductionCalculations(boolean normalize){ + this.normalize = normalize; + } + + public void addAppropriateHeaders(ArrayList roiList, RangeOfImage rangeOfImage, + ReducedData reducedData, + LinkedTreeMap> channelInfo){ + for (int r = 0; r < roiList.size(); r++){ + for (int c = rangeOfImage.channelStart; c <= rangeOfImage.channelEnd; c++){ //Last channel is domain channel, not variable + String stringC = String.valueOf(c - 1); + String channelName = channelInfo != null && channelInfo.containsKey(stringC) ? channelInfo.get(stringC).get("Name") : String.valueOf(c); + reducedData.channelNames.put(c - 1, channelName); + reducedData.roiNames.put(r, roiList.get(r).getName()); + } + } + } + + /** + * Set position in image with specific ROI, and then with the appropriate reduced data store, perform the calculation + * and add it to reduced data. + * @param imagePlus + * @param roiList + * @param normalizationValue + * @param rangeOfImage + */ + void calculateStatistics(ImagePlus imagePlus, ArrayList roiList, + HashMap normalizationValue, + ReducedData reducedData, + RangeOfImage rangeOfImage, AtomicBoolean continueOperation){ + for (int roi = 0; roi < roiList.size(); roi++) { + imagePlus.setRoi(roiList.get(roi)); + for (int t = rangeOfImage.timeStart; t <= rangeOfImage.timeEnd; t++){ + for (int z = rangeOfImage.zStart; z <= rangeOfImage.zEnd; z++){ + for (int c = rangeOfImage.channelStart; c <= rangeOfImage.channelEnd; c++){ + if (!continueOperation.get()){ + return; + } + imagePlus.setPosition(c, z, t); + int nT = t - rangeOfImage.timeStart; + int nZ = z - rangeOfImage.zStart; + int nC = c - rangeOfImage.channelStart; + double calculatedValue; + for (SelectMeasurements.AvailableMeasurements measurement : reducedData.measurements){ + switch (measurement){ + case AVERAGE: + calculatedValue = imagePlus.getStatistics().mean; + break; + case STD_DEV: + calculatedValue = imagePlus.getStatistics().stdDev; + break; + case MAX_INTENSITY: + calculatedValue = imagePlus.getStatistics().max; + break; + case MIN_INTENSITY: + calculatedValue = imagePlus.getStatistics().min; + break; + default: + throw new RuntimeException("Unknown measurement type selected."); + } + if (normalize){ + calculatedValue = calculatedValue / normalizationValue.get(roiList.get(roi).getName() + c); + } + reducedData.putDataPoint(calculatedValue, nT, nZ, nC, roi, measurement); + } + } + } + } + } + } + + HashMap calculateNormalValue(ImagePlus imagePlus, RangeOfImage normRange, + ArrayList roiList, RangeOfImage rangeOfImage){ + int startT = normRange.timeStart; + int endT = normRange.timeEnd; + HashMap result = new HashMap<>(); + for (Roi roi : roiList){ + imagePlus.setRoi(roi); + for (int c = rangeOfImage.channelStart; c <= rangeOfImage.channelEnd; c++){ + double normal = 0; + for (int t = startT; t <= endT; t++){ + double zAverage = 0; + for (int z = rangeOfImage.zStart; z <= rangeOfImage.zEnd; z++){ + imagePlus.setPosition(c, z, t); + zAverage += imagePlus.getProcessor().getStatistics().mean; + } + zAverage = zAverage / (rangeOfImage.zEnd - rangeOfImage.zStart + 1); + normal += zAverage; + } + normal = normal / (endT - startT + 1); // inclusive of final point + result.put(roi.getName() + c, normal); + } + } + return result; + } + +} diff --git a/view-simulation-results/src/main/java/org/vcell/N5/retrieving/LoadingFactory.java b/view-simulation-results/src/main/java/org/vcell/N5/retrieving/LoadingFactory.java deleted file mode 100644 index a673257..0000000 --- a/view-simulation-results/src/main/java/org/vcell/N5/retrieving/LoadingFactory.java +++ /dev/null @@ -1,153 +0,0 @@ -package org.vcell.N5.retrieving; - -import com.amazonaws.AbortedException; -import com.amazonaws.http.timers.client.SdkInterruptedException; -import ij.ImagePlus; -import ij.plugin.Duplicator; -import org.scijava.log.Logger; -import org.vcell.N5.N5ImageHandler; -import org.vcell.N5.UI.ControlButtonsPanel; -import org.vcell.N5.UI.RangeSelector; -import org.vcell.N5.UI.MainPanel; - -import javax.swing.*; -import javax.swing.event.EventListenerList; -import java.awt.*; -import java.io.File; -import java.io.IOException; -import java.util.ArrayList; -import java.util.HashMap; - -public class LoadingFactory implements SimLoadingEventCreator { - private static final EventListenerList eventListenerList = new EventListenerList(); - - private final ControlButtonsPanel controlButtonsPanel = MainPanel.controlButtonsPanel; - - private final HashMap openingSimulations = new HashMap<>(); - private final Object openSimulationsLock = new Object(); - - private static final Logger logger = N5ImageHandler.getLogger(RangeSelector.class); - - public void openN5FileDataset(ArrayList filesToOpen, boolean openInMemory){ - controlButtonsPanel.allowCancel(true); - MainPanel.changeCursor(new Cursor(Cursor.WAIT_CURSOR)); - - for (int i = 0; i < filesToOpen.size(); i++){ - SimResultsLoader simResultsLoader = filesToOpen.get(i); - Thread openThread = new Thread(() -> { - try{ - simResultsLoader.createS3ClientAndReader(); - notifySimIsLoading(simResultsLoader); - RangeSelector rangeSelector; - if (openInMemory){ - ArrayList dimensions = simResultsLoader.getN5Dimensions(); - rangeSelector = new RangeSelector(dimensions.get(2), dimensions.get(3), dimensions.get(4), simResultsLoader.userSetFileName); - rangeSelector.displayRangeMenu(); - if (!rangeSelector.cancel){ - openInMemory(simResultsLoader, rangeSelector); - } - rangeSelector.dispose(); - } else{ - ImagePlus imagePlus = simResultsLoader.getImgPlusFromN5File(); - imagePlus.show(); - } - } - catch (RuntimeException e) { - if (e.getCause().getCause().getCause() instanceof SdkInterruptedException || - e.getCause().getCause() instanceof AbortedException){ - logger.debug("Simulation stopped loading"); - } else { - throw new RuntimeException(e); - } - } - catch (Exception e){ - throw new RuntimeException(e); - } finally { - MainPanel.changeCursor(new Cursor(Cursor.DEFAULT_CURSOR)); - controlButtonsPanel.enableCriticalButtons(true); - notifySimIsDoneLoading(simResultsLoader); - synchronized (openSimulationsLock){ - openingSimulations.remove(simResultsLoader.exportID); - } - } - }); - openThread.setName("Opening sim number: " + i + ". With id: " + simResultsLoader.exportID); - synchronized (openSimulationsLock){ - openingSimulations.put(simResultsLoader.exportID, openThread); - } - openThread.start(); - } - } - - private void openInMemory(SimResultsLoader simResultsLoader, RangeSelector rangeSelector) throws IOException { - ImagePlus imagePlus = simResultsLoader.getImgPlusFromN5File(); - long start = System.currentTimeMillis(); - logger.debug("Loading Virtual N5 File " + simResultsLoader.userSetFileName + " Into Memory"); - imagePlus = new Duplicator().run(imagePlus, rangeSelector.startC, rangeSelector.endC, rangeSelector.startZ, - rangeSelector.endZ, rangeSelector.startT, rangeSelector.endT); - long end = System.currentTimeMillis(); - logger.debug("Loaded Virtual N5 File " + simResultsLoader.userSetFileName + " Into Memory taking: " + ((end - start)/ 1000) + "s"); - imagePlus.show(); - } - - public void stopOpeningSimulation(String exportID){ - Thread stopOtherThread = new Thread(() -> { - synchronized (openSimulationsLock){ - openingSimulations.get(exportID).interrupt(); - openingSimulations.remove(exportID); - } - }); - stopOtherThread.start(); - } - - public void openLocalN5FS(ArrayList filesToOpen){ - controlButtonsPanel.enableCriticalButtons(true); - JFileChooser fileChooser = new JFileChooser(); - fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); - fileChooser.setAcceptAllFileFilterUsed(false); - int result = fileChooser.showOpenDialog(null); - if (result == JFileChooser.APPROVE_OPTION){ - File file = fileChooser.getSelectedFile(); - MainPanel.changeCursor(new Cursor(Cursor.WAIT_CURSOR)); - Thread openN5FileDataset = new Thread(() -> { - try{ - for(SimResultsLoader simResultsLoader: filesToOpen){ - simResultsLoader.setSelectedLocalFile(file); - ImagePlus imagePlus = simResultsLoader.getImgPlusFromLocalN5File(); - imagePlus.show(); - } - } catch (IOException ex) { - throw new RuntimeException(ex); - } finally { - SwingUtilities.invokeLater(new Runnable() { - @Override - public void run() { - MainPanel.changeCursor(new Cursor(Cursor.DEFAULT_CURSOR)); - controlButtonsPanel.enableCriticalButtons(true); - } - }); - } - }); - openN5FileDataset.start(); - } - } - - @Override - public void addSimLoadingListener(SimLoadingListener simLoadingListener) { - eventListenerList.add(SimLoadingListener.class, simLoadingListener); - } - - @Override - public void notifySimIsLoading(SimResultsLoader simResultsLoader) { - for (SimLoadingListener simLoadingListener: eventListenerList.getListeners(SimLoadingListener.class)){ - simLoadingListener.simIsLoading(simResultsLoader.rowNumber, simResultsLoader.exportID); - } - } - - @Override - public void notifySimIsDoneLoading(SimResultsLoader simResultsLoader) { - for (SimLoadingListener simLoadingListener: eventListenerList.getListeners(SimLoadingListener.class)){ - simLoadingListener.simFinishedLoading(simResultsLoader.rowNumber, simResultsLoader.exportID); - } - } -} diff --git a/view-simulation-results/src/main/java/org/vcell/N5/retrieving/LoadingManager.java b/view-simulation-results/src/main/java/org/vcell/N5/retrieving/LoadingManager.java new file mode 100644 index 0000000..c44ccac --- /dev/null +++ b/view-simulation-results/src/main/java/org/vcell/N5/retrieving/LoadingManager.java @@ -0,0 +1,172 @@ +package org.vcell.N5.retrieving; + +import com.amazonaws.AbortedException; +import ij.ImagePlus; +import org.scijava.log.Logger; +import org.vcell.N5.N5ImageHandler; +import org.vcell.N5.UI.ControlButtonsPanel; +import org.vcell.N5.UI.RangeSelector; +import org.vcell.N5.UI.MainPanel; +import org.vcell.N5.reduction.GUI.DataReductionGUI; +import org.vcell.N5.reduction.DataReductionManager; + +import javax.swing.*; +import javax.swing.event.EventListenerList; +import java.awt.*; +import java.io.File; +import java.io.IOException; +import java.util.ArrayList; +import java.util.HashMap; + +public class LoadingManager implements SimLoadingEventCreator { + private static final EventListenerList eventListenerList = new EventListenerList(); + + private final ControlButtonsPanel controlButtonsPanel = MainPanel.controlButtonsPanel; + + private final HashMap openingSimulations = new HashMap<>(); + private final Object openSimulationsLock = new Object(); + private DataReductionManager dataReductionWriter = null; + + private static final Logger logger = N5ImageHandler.getLogger(RangeSelector.class); + + public void openN5FileDataset(ArrayList filesToOpen, boolean openInMemory, boolean dataReduction){ + RangeSelector rangeSelector = new RangeSelector(); + DataReductionGUI dataReductionGUI = null; + if (dataReduction || openInMemory){ + SimResultsLoader firstSim = filesToOpen.get(0); + firstSim.createS3ClientAndReader(); + ArrayList dimensions = firstSim.getN5Dimensions(); + if (dataReduction){ + dataReductionGUI = new DataReductionGUI(filesToOpen, dimensions.get(2), dimensions.get(3), dimensions.get(4)); + dataReductionWriter = dataReductionGUI.shouldContinueWithProcess() ? new DataReductionManager(dataReductionGUI.createSubmission()) : null; + } else { + rangeSelector.displayRangeMenu(dimensions.get(2), dimensions.get(3), dimensions.get(4)); + } + } + boolean dataReductionOkay = dataReduction && dataReductionGUI.shouldContinueWithProcess(); + if (dataReductionOkay || !dataReduction){ + MainPanel.changeCursor(new Cursor(Cursor.WAIT_CURSOR)); + for (int i = 0; i < filesToOpen.size(); i++){ + SimResultsLoader simResultsLoader = filesToOpen.get(i); + Thread openThread = new Thread(() -> { + ImagePlus imagePlus = null; + try{ + simResultsLoader.createS3ClientAndReader(); + notifySimIsLoading(simResultsLoader); + if (openInMemory){ + if (!rangeSelector.cancel){ + imagePlus = simResultsLoader.openInMemory(rangeSelector); + } + } else{ + simResultsLoader.loadImageFromN5File(); + imagePlus = simResultsLoader.getImagePlus(); + } + } + catch (RuntimeException e) { + simResultsLoader.setTagToCanceled(); + MainPanel.n5ExportTable.removeSpecificRowFromLoadingRows(simResultsLoader.rowNumber); + if (e instanceof AbortedException){ + logger.debug("Simulation stopped loading"); + } else { + throw new RuntimeException(e); + } + } finally { + MainPanel.changeCursor(new Cursor(Cursor.DEFAULT_CURSOR)); + notifySimIsDoneLoading(simResultsLoader, imagePlus); + synchronized (openSimulationsLock){ + openingSimulations.remove(simResultsLoader.exportID); + } + } + }); + openThread.setName("Opening sim number: " + i + ". With id: " + simResultsLoader.exportID); + synchronized (openSimulationsLock){ + openingSimulations.put(simResultsLoader.exportID, openThread); + } + openThread.start(); + } + } + } + + public void stopAllImagesAndAnalysis(){ + Thread stopEverything = new Thread(() -> { + synchronized (openSimulationsLock){ + for (String threadName : openingSimulations.keySet()){ + openingSimulations.get(threadName).interrupt(); + openingSimulations.remove(threadName); + } + } + if (dataReductionWriter != null){ + dataReductionWriter.stopAllThreads(); + } + }); + stopEverything.start(); + } + + + public void stopLoadingImage(String exportID){ + Thread stopOtherThread = new Thread(() -> { + synchronized (openSimulationsLock){ + if (openingSimulations.containsKey(exportID)){ + openingSimulations.get(exportID).interrupt(); + openingSimulations.remove(exportID); + } + } + }); + stopOtherThread.start(); + } + + public void openLocalN5FS(ArrayList filesToOpen){ + controlButtonsPanel.enableAllButtons(true); + JFileChooser fileChooser = new JFileChooser(); + fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); + fileChooser.setAcceptAllFileFilterUsed(false); + int result = fileChooser.showOpenDialog(null); + if (result == JFileChooser.APPROVE_OPTION){ + File file = fileChooser.getSelectedFile(); + MainPanel.changeCursor(new Cursor(Cursor.WAIT_CURSOR)); + Thread openN5FileDataset = new Thread(() -> { + try{ + for(SimResultsLoader simResultsLoader: filesToOpen){ + simResultsLoader.setSelectedLocalFile(file); + ImagePlus imagePlus = simResultsLoader.getImgPlusFromLocalN5File(); + imagePlus.show(); + } + } catch (IOException ex) { + throw new RuntimeException(ex); + } finally { + SwingUtilities.invokeLater(new Runnable() { + @Override + public void run() { + MainPanel.changeCursor(new Cursor(Cursor.DEFAULT_CURSOR)); + controlButtonsPanel.enableAllButtons(true); + } + }); + } + }); + openN5FileDataset.start(); + } + } + + @Override + public void addSimLoadingListener(SimLoadingListener simLoadingListener) { + eventListenerList.add(SimLoadingListener.class, simLoadingListener); + } + + public void removeFromSimLoadingListener(SimLoadingListener simLoadingListener){ + eventListenerList.remove(SimLoadingListener.class, simLoadingListener); + } + + @Override + public void notifySimIsLoading(SimResultsLoader simResultsLoader) { + for (SimLoadingListener simLoadingListener: eventListenerList.getListeners(SimLoadingListener.class)){ + simLoadingListener.simIsLoading(simResultsLoader.rowNumber, simResultsLoader.exportID); + } + } + + @Override + public void notifySimIsDoneLoading(SimResultsLoader simResultsLoader, ImagePlus imagePlus) { + for (SimLoadingListener simLoadingListener: eventListenerList.getListeners(SimLoadingListener.class)){ + simLoadingListener.simFinishedLoading(simResultsLoader); + } + } +} diff --git a/view-simulation-results/src/main/java/org/vcell/N5/retrieving/SimLoadingEventCreator.java b/view-simulation-results/src/main/java/org/vcell/N5/retrieving/SimLoadingEventCreator.java index 2a0a357..a69ac7e 100644 --- a/view-simulation-results/src/main/java/org/vcell/N5/retrieving/SimLoadingEventCreator.java +++ b/view-simulation-results/src/main/java/org/vcell/N5/retrieving/SimLoadingEventCreator.java @@ -1,10 +1,12 @@ package org.vcell.N5.retrieving; +import ij.ImagePlus; + public interface SimLoadingEventCreator { public void addSimLoadingListener(SimLoadingListener simLoadingListener); public void notifySimIsLoading(SimResultsLoader simResultsLoader); - public void notifySimIsDoneLoading(SimResultsLoader simResultsLoader); + public void notifySimIsDoneLoading(SimResultsLoader simResultsLoader, ImagePlus imagePlus); } diff --git a/view-simulation-results/src/main/java/org/vcell/N5/retrieving/SimLoadingListener.java b/view-simulation-results/src/main/java/org/vcell/N5/retrieving/SimLoadingListener.java index d2ddf09..e517f11 100644 --- a/view-simulation-results/src/main/java/org/vcell/N5/retrieving/SimLoadingListener.java +++ b/view-simulation-results/src/main/java/org/vcell/N5/retrieving/SimLoadingListener.java @@ -1,11 +1,13 @@ package org.vcell.N5.retrieving; +import ij.ImagePlus; + import java.util.EventListener; public interface SimLoadingListener extends EventListener { public void simIsLoading(int itemRow, String exportID); - public void simFinishedLoading(int itemRow, String exportID); + public void simFinishedLoading(SimResultsLoader loadedResult); } diff --git a/view-simulation-results/src/main/java/org/vcell/N5/retrieving/SimResultsLoader.java b/view-simulation-results/src/main/java/org/vcell/N5/retrieving/SimResultsLoader.java index a3e9495..c005edf 100644 --- a/view-simulation-results/src/main/java/org/vcell/N5/retrieving/SimResultsLoader.java +++ b/view-simulation-results/src/main/java/org/vcell/N5/retrieving/SimResultsLoader.java @@ -8,8 +8,10 @@ import com.amazonaws.services.s3.AmazonS3; import com.amazonaws.services.s3.AmazonS3ClientBuilder; import com.google.gson.GsonBuilder; +import com.google.gson.internal.LinkedTreeMap; import ij.ImagePlus; import ij.plugin.ContrastEnhancer; +import ij.plugin.Duplicator; import net.imglib2.cache.img.CachedCellImg; import net.imglib2.img.display.imagej.ImageJFunctions; import net.imglib2.type.numeric.real.DoubleType; @@ -21,9 +23,9 @@ import org.janelia.saalfeldlab.n5.N5KeyValueReader; import org.janelia.saalfeldlab.n5.N5Reader; import org.janelia.saalfeldlab.n5.imglib2.N5Utils; -import org.janelia.saalfeldlab.n5.s3.N5AmazonS3Reader; import org.scijava.log.Logger; import org.vcell.N5.N5ImageHandler; +import org.vcell.N5.UI.RangeSelector; import org.vcell.N5.library.extensions.S3KeyValueAccess; import org.vcell.N5.library.extensions.SimCacheLoader; @@ -39,34 +41,33 @@ import java.security.KeyStoreException; import java.security.NoSuchAlgorithmException; import java.util.ArrayList; -import java.util.Arrays; import java.util.HashMap; public class SimResultsLoader { private File selectedLocalFile; - private AmazonS3 s3Client; - private String bucketName; - private String s3ObjectKey; private URI uri; private String dataSetChosen; - public String userSetFileName = null; private static final String defaultS3Region = "site2-low"; - private N5Reader n5AmazonS3Reader; - + private N5Reader n5AmazonS3Reader = null; private static final Logger logger = N5ImageHandler.getLogger(SimResultsLoader.class); public static AmazonS3ClientBuilder s3ClientBuilder; + private ImagePlus imagePlus = null; + public OpenTag openTag; + + + public String userSetFileName = null; public int rowNumber; public String exportID; public SimResultsLoader(){ - + openTag = OpenTag.TEST; } - public SimResultsLoader(String stringURI, String userSetFileName, int rowNumber, String exportID){ - this(stringURI, userSetFileName); + public SimResultsLoader(String stringURI, String userSetFileName, int rowNumber, String exportID, OpenTag openTag){ + this(stringURI, userSetFileName, openTag); this.rowNumber = rowNumber; this.exportID = exportID; } - public SimResultsLoader(String stringURI, String userSetFileName){ + public SimResultsLoader(String stringURI, String userSetFileName, OpenTag openTag){ uri = URI.create(stringURI); this.userSetFileName = userSetFileName; if(!(uri.getQuery() == null)){ @@ -77,77 +78,112 @@ public SimResultsLoader(String stringURI, String userSetFileName){ throw new RuntimeException(e); } } + this.openTag = openTag; } - void createS3ClientAndReader(){ + ///////////////////////////////// + // Loading Simulation Results // + /////////////////////////////// + + public void createS3ClientAndReader(){ logger.debug("Creating S3 Client with url: " + uri); if (uri.getHost().equals("minikube.remote") || uri.getHost().equals("minikube.island")){ - SSLContext sslContext = null; - try { - sslContext = SSLContexts.custom().loadTrustMaterial(new TrustSelfSignedStrategy()).build(); - } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException ex) { - throw new RuntimeException("Custom ssl context not functional.",ex); - } - HostnameVerifier hostnameVerifier = new HostnameVerifier() { - @Override - public boolean verify(String hostname, SSLSession session) { - return true; - } - }; - ConnectionSocketFactory connectionSocketFactory = new SdkTLSSocketFactory(sslContext, hostnameVerifier); - ClientConfiguration clientConfiguration = new ClientConfiguration(); - clientConfiguration.getApacheHttpClientConfig().setSslSocketFactory(connectionSocketFactory); - s3ClientBuilder.setClientConfiguration(clientConfiguration); + allowInsecureConnections(); } + ////////////////////////////////////////////// + // assume it is one of our URLs // + // http://vcell:8000/bucket/object/object2 // + ////////////////////////////////////////////// - // assume it is one of our URLs - // http://vcell:8000/bucket/object/object2 String[] pathSubStrings = uri.getPath().split("/", 3); - this.s3ObjectKey = pathSubStrings[2]; - this.bucketName = pathSubStrings[1]; + String s3ObjectKey = pathSubStrings[2]; + String bucketName = pathSubStrings[1]; s3ClientBuilder.withPathStyleAccessEnabled(true); logger.debug("Creating S3 Client with Path Based Buckets"); s3ClientBuilder.withCredentials(new AWSStaticCredentialsProvider(new AnonymousAWSCredentials())); s3ClientBuilder.withEndpointConfiguration(new AwsClientBuilder.EndpointConfiguration(uri.getScheme() + "://" + uri.getAuthority(), defaultS3Region)); - this.s3Client = s3ClientBuilder.build(); + AmazonS3 s3Client = s3ClientBuilder.build(); S3KeyValueAccess amazonS3KeyValueAccess = new S3KeyValueAccess(s3Client, bucketName, false); n5AmazonS3Reader = new N5KeyValueReader(amazonS3KeyValueAccess, s3ObjectKey, new GsonBuilder(), false); } + private void allowInsecureConnections(){ + SSLContext sslContext = null; + try { + sslContext = SSLContexts.custom().loadTrustMaterial(new TrustSelfSignedStrategy()).build(); + } catch (NoSuchAlgorithmException | KeyManagementException | KeyStoreException ex) { + throw new RuntimeException("Custom ssl context not functional.",ex); + } + HostnameVerifier hostnameVerifier = new HostnameVerifier() { + @Override + public boolean verify(String hostname, SSLSession session) { + return true; + } + }; + ConnectionSocketFactory connectionSocketFactory = new SdkTLSSocketFactory(sslContext, hostnameVerifier); + ClientConfiguration clientConfiguration = new ClientConfiguration(); + clientConfiguration.getApacheHttpClientConfig().setSslSocketFactory(connectionSocketFactory); + s3ClientBuilder.setClientConfiguration(clientConfiguration); + } + /** By default N5 library assumes that S3 URL's are formated in the standard amazon format. That being https://BUCKETNAME.s3.REGION.amazonaws.com {@link org.janelia.saalfeldlab.n5.s3.AmazonS3Utils}. Because our objects don't originate from amazon this is not a format we can possibly mimic, so we have to use path based buckets because it's the fallback style chosen by the N5 libraries if standard format is unavailable. */ - public ImagePlus getImgPlusFromN5File() throws IOException { -// N5AmazonS3Reader n5AmazonS3Reader = new N5AmazonS3Reader(s3Client, bucketName, "/" + s3ObjectKey); + public ImagePlus getImagePlus(){ + if (imagePlus != null){ + return imagePlus; + } + if (n5AmazonS3Reader == null){ + createS3ClientAndReader(); + } + loadImageFromN5File(); + return imagePlus; + } + + public void loadImageFromN5File() { long start = System.currentTimeMillis(); logger.debug("Reading N5 File " + userSetFileName + " Into Virtual Image"); if (userSetFileName == null || userSetFileName.isEmpty()){ userSetFileName = n5AmazonS3Reader.getAttribute(dataSetChosen, "name", String.class); } - SimCacheLoader simCacheLoader = SimCacheLoader.factoryDefault(n5AmazonS3Reader, dataSetChosen); CachedCellImg cachedCellImg = simCacheLoader.createCachedCellImage(); - ImagePlus imagePlus = ImageJFunctions.wrap(cachedCellImg, userSetFileName); + imagePlus = ImageJFunctions.wrap(cachedCellImg, userSetFileName); simCacheLoader.setImagesResponsibleFor(imagePlus); long end = System.currentTimeMillis(); logger.debug("Read N5 File " + userSetFileName + " Into ImageJ taking: " + ((end - start) / 1000.0) + "s"); setUnits(n5AmazonS3Reader, imagePlus); - imagePlus.setProperty("channelInfo", n5AmazonS3Reader.getAttribute(dataSetChosen, "channelInfo", HashMap.class)); + imagePlus.setProperty("channelInfo", getChannelInfo()); imagePlus.setProperty("maskInfo", n5AmazonS3Reader.getAttribute(dataSetChosen, "maskMapping", HashMap.class)); imagePlus.setZ(Math.floorDiv(imagePlus.getNSlices(), 2)); imagePlus.setT(Math.floorDiv(imagePlus.getNFrames(), 2)); new ContrastEnhancer().stretchHistogram(imagePlus, 1); + } + + public ImagePlus openInMemory(RangeSelector rangeSelector){ + loadImageFromN5File(); + long start = System.currentTimeMillis(); + logger.debug("Loading Virtual N5 File " + userSetFileName + " Into Memory"); + imagePlus = new Duplicator().run(imagePlus, rangeSelector.startC, rangeSelector.endC, rangeSelector.startZ, + rangeSelector.endZ, rangeSelector.startT, rangeSelector.endT); + long end = System.currentTimeMillis(); + logger.debug("Loaded Virtual N5 File " + userSetFileName + " Into Memory taking: " + ((end - start)/ 1000) + "s"); return imagePlus; } + + //////////////// + // Properties // + //////////////// + private void setUnits(N5Reader n5Reader, ImagePlus imagePlus){ try{ double pixelWidth = n5Reader.getAttribute(dataSetChosen, "pixelWidth", double.class); @@ -170,38 +206,35 @@ public ArrayList getN5Dimensions(){ return n5AmazonS3Reader.getAttribute(dataSetChosen, "dimensions", ArrayList.class); } + public LinkedTreeMap> getChannelInfo(){ + return (LinkedTreeMap>) n5AmazonS3Reader.getAttribute(dataSetChosen, "channelInfo", LinkedTreeMap.class); + } + void setDataSetChosen(String dataSetChosen) { this.dataSetChosen = dataSetChosen; } + public void setTagToCanceled(){ + openTag = OpenTag.CANCELED; + } + + ///////////////////////// + // Local File Reading // + //////////////////////// + void setSelectedLocalFile(File selectedLocalFile){ this.selectedLocalFile = selectedLocalFile; } - ArrayList getS3N5DatasetList() throws IOException { - // used as a flag to tell that remote access is occurring, and that there is no local files - try(N5AmazonS3Reader n5AmazonS3Reader = new N5AmazonS3Reader(this.s3Client, this.bucketName)) { - return new ArrayList<>(Arrays.asList(n5AmazonS3Reader.deepListDatasets(this.s3ObjectKey))); - } - } ImagePlus getImgPlusFromLocalN5File() throws IOException { N5Reader n5Reader = new N5FSReader(selectedLocalFile.getPath()); return ImageJFunctions.wrap((CachedCellImg) N5Utils.open(n5Reader, dataSetChosen), userSetFileName); } - ArrayList getN5DatasetList() throws IOException { - // auto closes reader - logger.debug("Getting List of N5 Datasets"); - try (N5FSReader n5Reader = new N5FSReader(selectedLocalFile.getPath())) { - String[] metaList = n5Reader.deepList("/"); - ArrayList fList = new ArrayList<>(); - for (String s : metaList) { - if (n5Reader.datasetExists(s)) { - fList.add(s); - }; - } - logger.debug("Got List of N5 Datasets"); - return fList; - } + public enum OpenTag{ + VIEW, + DATA_REDUCTION, + CANCELED, + TEST } } diff --git a/view-simulation-results/src/test/java/org/vcell/N5/reduction/ReductionCalculationsTest.java b/view-simulation-results/src/test/java/org/vcell/N5/reduction/ReductionCalculationsTest.java new file mode 100644 index 0000000..314c356 --- /dev/null +++ b/view-simulation-results/src/test/java/org/vcell/N5/reduction/ReductionCalculationsTest.java @@ -0,0 +1,125 @@ +package org.vcell.N5.reduction; + +import com.amazonaws.services.s3.AmazonS3ClientBuilder; +import ij.ImagePlus; +import ij.gui.Roi; +import ij.io.RoiDecoder; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; +import org.vcell.N5.N5ImageHandler; +import org.vcell.N5.reduction.DTO.RangeOfImage; +import org.vcell.N5.reduction.DTO.ReducedData; +import org.vcell.N5.reduction.GUI.SelectMeasurements; +import org.vcell.N5.retrieving.LoadingManager; +import org.vcell.N5.retrieving.SimResultsLoader; + +import java.io.File; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.concurrent.atomic.AtomicBoolean; + +public class ReductionCalculationsTest { + // First two are SimROI, last two are LabROI + private final double[][] labMeans2D = new double[][]{{6, 0, 18, 0}, {6.489, 0, 16.341, 0}, + {7.247, 0, 14.469, 0}}; //calculated through IJ measurement tool + + private final double[][] threeDMeans = new double[][]{{2884.526, 3102.159}, {3884.279, 4668.205}, {5016.744, 5524.792}, {4794.329, 5624.351}, + {4559.778, 5510.099} + }; + + private File getTestResourceFiles(String filePath){ + try { + URL url = ClassLoader.getSystemClassLoader().getResource(filePath); + return new File(url.toURI().getPath()); + } + catch (URISyntaxException e){ + throw new RuntimeException(e); + } + } + + @BeforeClass + public static void init(){ + N5ImageHandler.initializeLogService(); + SimResultsLoader.s3ClientBuilder = AmazonS3ClientBuilder.standard(); + N5ImageHandler.loadingManager = new LoadingManager(); + } + + private void compareExpectedCalculations(ImagePlus imagePlus, ArrayList roiList, double[][] expectedResults, + boolean normalizeMeasurementsBool, int nz, int nt, int nc){ + ReductionCalculations reductionCalculations = new ReductionCalculations(normalizeMeasurementsBool); + + RangeOfImage entireRange = new RangeOfImage(1, imagePlus.getNFrames(), 1, imagePlus.getNSlices(), 1, imagePlus.getNChannels()); + RangeOfImage normRange = new RangeOfImage(1, 1); + ReducedData reducedData = new ReducedData("", entireRange, roiList.size(), new ArrayList<>(Collections.singletonList(SelectMeasurements.AvailableMeasurements.AVERAGE))); + + HashMap norms = reductionCalculations.calculateNormalValue(imagePlus, normRange, roiList, entireRange); + reductionCalculations.calculateStatistics(imagePlus, roiList, norms, reducedData, entireRange, new AtomicBoolean(true)); + for (int c = 0; c < nc; c++){ + for (int r = 0; r < roiList.size(); r++){ + for (int t = 0; t < nt; t++){ + for (int z = 0; z < nz; z++){ + int row = (t * nz) + z; + int col = (r * nc) + c; + Assert.assertEquals(expectedResults[row][col], reducedData.getDataPoint(t, z, c, r, SelectMeasurements.AvailableMeasurements.AVERAGE), 0.0009); + } + } + } + } + } + + @Test + public void testMean2DCalculation(){ + // Ensure the mean calculated for each ROI, and each time point is what's to be expected + SimResultsLoader simResultsLoader = new SimResultsLoader("https://vcell.cam.uchc.edu/n5Data/ezequiel23/ddf7f4f0c77dffd.n5?dataSetName=4864003788", "test1", SimResultsLoader.OpenTag.TEST); + ImagePlus labResultImage2D = simResultsLoader.getImagePlus(); + + Roi labRoi = RoiDecoder.open(getTestResourceFiles("ROIs/Lab ROI.roi").getAbsolutePath()); + Roi simROI = RoiDecoder.open(getTestResourceFiles("ROIs/Sim ROI.roi").getAbsolutePath()); + ArrayList roiList = new ArrayList(){{add(labRoi); add(simROI);}}; + compareExpectedCalculations(labResultImage2D, roiList, labMeans2D, false, + 1, 3, 2); + } + + @Test + public void testMean3DCalculation(){ + ImagePlus mitosis = new ImagePlus(getTestResourceFiles("mitosis.tif").getAbsolutePath()); + Roi mitosisROI = RoiDecoder.open(getTestResourceFiles("ROIs/Mitosis Center.roi").getAbsolutePath()); + ArrayList roiList = new ArrayList(){{add(mitosisROI);}}; + compareExpectedCalculations(mitosis, roiList, threeDMeans, false, + 5, 1, 2); + } + + @Test + public void testMeanAndNormalization2DCalculation(){ + SimResultsLoader simResultsLoader = new SimResultsLoader("https://vcell.cam.uchc.edu/n5Data/ezequiel23/ddf7f4f0c77dffd.n5?dataSetName=4864003788", "test1", SimResultsLoader.OpenTag.TEST); + ImagePlus labResultImage2D = simResultsLoader.getImagePlus(); + Roi labRoi = RoiDecoder.open(getTestResourceFiles("ROIs/Lab ROI.roi").getAbsolutePath()); + Roi simROI = RoiDecoder.open(getTestResourceFiles("ROIs/Sim ROI.roi").getAbsolutePath()); + ArrayList roiList = new ArrayList(){{add(labRoi); add(simROI);}}; + HashMap normValues = new HashMap<>(); + + for (Roi roi: roiList){ + labResultImage2D.setRoi(roi); + for (int c = 1; c <= labResultImage2D.getNChannels(); c++){ + labResultImage2D.setC(c); + normValues.put(roi.getName() + c, labResultImage2D.getStatistics().mean); + } + } + double[][] normalizedValues = new double[labMeans2D.length][labMeans2D[0].length]; + + for (int r = 0; r < labMeans2D.length; r++){ + for (int c =0; c < labMeans2D[0].length; c++){ + String roiName = c < 2 ? labRoi.getName() : simROI.getName(); + double normValue = c % 2 == 0 ? normValues.get(roiName + 1) : normValues.get(roiName + 2); + normalizedValues[r][c] = labMeans2D[r][c] / normValue; + } + } + + compareExpectedCalculations(labResultImage2D, roiList, normalizedValues, true, + 1, 3, 2); + } +} diff --git a/view-simulation-results/src/test/java/org/vcell/N5/retrieving/N5ImageHandlerTest.java b/view-simulation-results/src/test/java/org/vcell/N5/retrieving/N5ImageHandlerTest.java index 1403cb3..3f189a1 100644 --- a/view-simulation-results/src/test/java/org/vcell/N5/retrieving/N5ImageHandlerTest.java +++ b/view-simulation-results/src/test/java/org/vcell/N5/retrieving/N5ImageHandlerTest.java @@ -59,13 +59,6 @@ public void run(){ N5ImageHandler.initializeLogService(); } - @Test - public void testN5DatasetList() throws IOException { - SimResultsLoader simResultsLoader = new SimResultsLoader(); - simResultsLoader.setSelectedLocalFile(this.getTestResourceFiles(n5FileName)); - this.dataSetListTest(simResultsLoader.getN5DatasetList()); - } - @Test public void testGettingImgPlus() throws IOException { SimResultsLoader simResultsLoader = new SimResultsLoader(); @@ -82,9 +75,10 @@ public void testGettingImgPlus() throws IOException { public void testS3AlphaInstance() throws IOException{ N5DataSetFile[] n5DataSetFiles = N5DataSetFile.alphaTestFiles(); for(N5DataSetFile n5DataSetFile : n5DataSetFiles) { - SimResultsLoader simResultsLoader = new SimResultsLoader(n5DataSetFile.uri, ""); + SimResultsLoader simResultsLoader = new SimResultsLoader(n5DataSetFile.uri, "", SimResultsLoader.OpenTag.TEST); simResultsLoader.createS3ClientAndReader(); - ImagePlus imagePlus = simResultsLoader.getImgPlusFromN5File(); + simResultsLoader.loadImageFromN5File(); + ImagePlus imagePlus = simResultsLoader.getImagePlus(); //stats that have been preemptively calculated within VCell alphaStatsTest(imagePlus, n5DataSetFile, stats.HISTMAX); @@ -97,9 +91,10 @@ public void testS3AlphaInstance() throws IOException{ public void testS3AlphaInstanceLoadedIntoMemory() throws IOException { N5DataSetFile[] n5DataSetFiles = N5DataSetFile.alphaTestFiles(); for(N5DataSetFile n5DataSetFile : n5DataSetFiles) { - SimResultsLoader simResultsLoader = new SimResultsLoader(n5DataSetFile.uri, ""); + SimResultsLoader simResultsLoader = new SimResultsLoader(n5DataSetFile.uri, "", SimResultsLoader.OpenTag.TEST); simResultsLoader.createS3ClientAndReader(); - ImagePlus imagePlus = simResultsLoader.getImgPlusFromN5File(); + simResultsLoader.loadImageFromN5File(); + ImagePlus imagePlus = simResultsLoader.getImagePlus(); ImagePlus inMemory = new Duplicator().run(imagePlus); for (Object property : imagePlus.getProperties().keySet()){ inMemory.setProperty((String) property, imagePlus.getProperty((String) property)); @@ -118,9 +113,10 @@ interface PixelCalculations { public void testUnits() throws IOException { N5DataSetFile[] n5DataSetFiles = N5DataSetFile.alphaTestFiles(); for (N5DataSetFile n5DataSetFile: n5DataSetFiles){ - SimResultsLoader simResultsLoader = new SimResultsLoader(n5DataSetFile.uri, ""); + SimResultsLoader simResultsLoader = new SimResultsLoader(n5DataSetFile.uri, "", SimResultsLoader.OpenTag.TEST); simResultsLoader.createS3ClientAndReader(); - ImagePlus imagePlus = simResultsLoader.getImgPlusFromN5File(); + simResultsLoader.loadImageFromN5File(); + ImagePlus imagePlus = simResultsLoader.getImagePlus(); double areaOfPixel = imagePlus.getCalibration().getX(1) * imagePlus.getCalibration().getY(1); double totalArea = areaOfPixel * imagePlus.getWidth() * imagePlus.getHeight(); @@ -287,15 +283,6 @@ private ImagePlus setImageMask(ImagePlus imagePlus){ return imagePlus; } - - private void remoteN5ImgPlusTests(SimResultsLoader simResultsLoader) throws IOException { - ImagePlus imagePlus = simResultsLoader.getImgPlusFromN5File(); - dataSetListTest(simResultsLoader.getS3N5DatasetList()); - fiveDStackTests(imagePlus); - imagePlus = new Duplicator().run(imagePlus); //Tests taking the N5 file from streaming to in memory - fiveDStackTests(imagePlus); - } - private void fiveDStackTests(ImagePlus variableImgPlus){ ImagePlus controlImgPlus = Opener.openUsingBioFormats(this.getTestResourceFiles("mitosis.tif").getAbsolutePath()); int[] variableDimensions = variableImgPlus.getDimensions(); diff --git a/view-simulation-results/src/test/resources/ROIs/Lab ROI.roi b/view-simulation-results/src/test/resources/ROIs/Lab ROI.roi new file mode 100644 index 0000000..6e4170b Binary files /dev/null and b/view-simulation-results/src/test/resources/ROIs/Lab ROI.roi differ diff --git a/view-simulation-results/src/test/resources/ROIs/Mitosis Center.roi b/view-simulation-results/src/test/resources/ROIs/Mitosis Center.roi new file mode 100644 index 0000000..15225b4 Binary files /dev/null and b/view-simulation-results/src/test/resources/ROIs/Mitosis Center.roi differ diff --git a/view-simulation-results/src/test/resources/ROIs/Sim ROI.roi b/view-simulation-results/src/test/resources/ROIs/Sim ROI.roi new file mode 100644 index 0000000..c3b6344 Binary files /dev/null and b/view-simulation-results/src/test/resources/ROIs/Sim ROI.roi differ