From 773f1f542b3d41842df6aa2cb77c857fa686705c Mon Sep 17 00:00:00 2001 From: Douglas Gillespie <50671166+douggillespie@users.noreply.github.com> Date: Sun, 24 Sep 2023 21:59:15 +0100 Subject: [PATCH] Species map i/o --- src/tethys/TethysControl.java | 13 ++ src/tethys/detection/DetectionsHandler.java | 4 + src/tethys/pamdata/AutoTethysProvider.java | 3 + .../species/DataBlockSpeciesManager.java | 25 +++ src/tethys/species/SpeciesMapManager.java | 150 +++++++++++++++++- .../species/swing/DataBlockSpeciesDialog.java | 16 ++ .../species/swing/DataBlockSpeciesPanel.java | 4 +- src/tethys/swing/DetectionsExportPanel.java | 17 ++ 8 files changed, 230 insertions(+), 2 deletions(-) diff --git a/src/tethys/TethysControl.java b/src/tethys/TethysControl.java index bd6e70f5..81270ce0 100644 --- a/src/tethys/TethysControl.java +++ b/src/tethys/TethysControl.java @@ -51,6 +51,7 @@ import tethys.output.TethysExportParams; import tethys.output.TethysExporter; import tethys.output.swing.TethysExportDialog; import tethys.species.ITISFunctions; +import tethys.species.SpeciesMapManager; import tethys.swing.ProjectDeploymentsDialog; import tethys.swing.TethysTabPanel; import tethys.swing.XMLStringView; @@ -200,6 +201,18 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet } }); tethysMenu.add(showDeps); + + tethysMenu.addSeparator(); + JMenuItem mapItem = new JMenuItem("Export species maps ..."); + mapItem.setToolTipText("Export all species maps (PAMGuard codes to ITIS codes to file for import into other configurations"); + mapItem.addActionListener(SpeciesMapManager.getInstance().getExportAction(parentFrame)); + tethysMenu.add(mapItem); + + mapItem = new JMenuItem("Import species maps ..."); + mapItem.setToolTipText("Import species maps (PAMGuard codes to ITIS codes to file for import into other configurations"); + mapItem.addActionListener(SpeciesMapManager.getInstance().getImportAction(parentFrame)); + tethysMenu.add(mapItem); + return tethysMenu; } diff --git a/src/tethys/detection/DetectionsHandler.java b/src/tethys/detection/DetectionsHandler.java index 659b9c24..7638c2d2 100644 --- a/src/tethys/detection/DetectionsHandler.java +++ b/src/tethys/detection/DetectionsHandler.java @@ -6,9 +6,11 @@ import java.util.List; import javax.swing.SwingWorker; import PamController.PamControlledUnit; +import PamController.PamController; import PamController.PamguardVersionInfo; import PamModel.PamPluginInterface; import PamUtils.PamCalendar; +import PamView.dialog.PamDialog; import PamView.dialog.warn.WarnOnce; import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; @@ -40,6 +42,7 @@ import tethys.niluswraps.TethysCollections; import tethys.output.StreamExportParams; import tethys.output.TethysExportParams; import tethys.pamdata.TethysDataProvider; +import tethys.species.DataBlockSpeciesManager; /** * Functions for handling output of Detections documents. @@ -203,6 +206,7 @@ public class DetectionsHandler { * @param exportWorkerCard */ public void startExportThread(PamDataBlock pamDataBlock, StreamExportParams streamExportParams, DetectionExportObserver exportObserver) { + checkGranularity(pamDataBlock, streamExportParams); tethysControl.getTethysExportParams().setStreamParams(pamDataBlock, streamExportParams); activeExport = true; diff --git a/src/tethys/pamdata/AutoTethysProvider.java b/src/tethys/pamdata/AutoTethysProvider.java index 0d7d46e8..49ede503 100644 --- a/src/tethys/pamdata/AutoTethysProvider.java +++ b/src/tethys/pamdata/AutoTethysProvider.java @@ -247,6 +247,9 @@ abstract public class AutoTethysProvider implements TethysDataProvider { Detection detection = new Detection(); detection.setStart(TethysTimeFuncs.xmlGregCalFromMillis(dataUnit.getTimeMilliseconds())); detection.setEnd(TethysTimeFuncs.xmlGregCalFromMillis(dataUnit.getEndTimeInMilliseconds())); + if (dataUnit.getEndTimeInMilliseconds() < dataUnit.getTimeMilliseconds()) { + System.out.printf("Error UID %d, end %s before start %s\n", dataUnit.getUID(), detection.getEnd(), detection.getStart()); + } DataBlockSpeciesManager speciesManager = pamDataBlock.getDatablockSpeciesManager(); SpeciesMapItem speciesItem = null; diff --git a/src/tethys/species/DataBlockSpeciesManager.java b/src/tethys/species/DataBlockSpeciesManager.java index 6b08d0ce..35947776 100644 --- a/src/tethys/species/DataBlockSpeciesManager.java +++ b/src/tethys/species/DataBlockSpeciesManager.java @@ -27,6 +27,7 @@ abstract public class DataBlockSpeciesManager { /** * The serialised bit. Always exists (or should be created) even if there * are no real species, via a defaultdefaultSpecies. + * Don't keep a local copy though since it may have been */ private DataBlockSpeciesMap datablockSpeciesMap; @@ -194,4 +195,28 @@ abstract public class DataBlockSpeciesManager { public void setDefaultSpeciesCode(String defaultName) { this.defaultSpeciesCode = defaultName; } + + /** + * Check the species map. Only return true if every species code + * has a map item. Otherwise it's not safe to export. + * @return null if all codes have a lookup, otherwise some sort of useful error information + */ + public String checkSpeciesMapError() { + ArrayList codes = getAllSpeciesCodes(); + if (codes == null || codes.size() == 0) { + return "No defined species codes"; // I guess that's OK ? + } + DataBlockSpeciesMap spMap = getDatablockSpeciesMap(); + if (spMap == null) { + return "No species map"; + } + + for (String aCode : codes) { + SpeciesMapItem item = spMap.getItem(aCode); + if (item == null) { + return "No Species item for species code " + aCode; + } + } + return null; + } } diff --git a/src/tethys/species/SpeciesMapManager.java b/src/tethys/species/SpeciesMapManager.java index 670e1926..c88828f2 100644 --- a/src/tethys/species/SpeciesMapManager.java +++ b/src/tethys/species/SpeciesMapManager.java @@ -1,17 +1,32 @@ package tethys.species; +import java.awt.Window; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; +import java.io.File; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; import java.io.Serializable; import java.util.ArrayList; +import javax.swing.JFileChooser; + import PamController.PamControlledUnitSettings; import PamController.PamController; +import PamController.PamFolders; import PamController.PamSettingManager; import PamController.PamSettings; +import PamUtils.PamFileFilter; +import PamView.dialog.warn.WarnOnce; import PamguardMVC.PamDataBlock; /** * Master manager for species maps which will eventually allow for export and import from XML - * documents, databases and other things ... + * documents, databases and other things ...
+ * (Perhaps not as XML, will simply output the serialized map - easier. * @author dg50 * */ @@ -19,10 +34,23 @@ public class SpeciesMapManager implements PamSettings { private static SpeciesMapManager singleInstance = null; + /** + * Synch object to survive multithreading. + */ private static Object synch = new Object(); + /** + * Map of all species maps. + */ private GlobalSpeciesMap globalSpeciesMap; + private JFileChooser ioFileChooser; + + /** + * file end type for map files + */ + public static final String mapFileEnd = ".spmap"; + private SpeciesMapManager() { PamSettingManager.getInstance().registerSettings(this); } @@ -101,4 +129,124 @@ public class SpeciesMapManager implements PamSettings { return false; } } + + public ActionListener getExportAction(Window parentFrame) { + return new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + exportSpeciesMaps(parentFrame); + } + }; + } + + public ActionListener getImportAction(Window parentFrame) { + return new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + importSpeciesMaps(parentFrame); + } + }; + } + + /** + * Export all species maps to a serialized object file. + * @param parentFrame + * @return + */ + public boolean exportSpeciesMaps(Window parentFrame) { + JFileChooser chooser = getFileChooser(); + int ans = chooser.showSaveDialog(parentFrame); + if (ans != JFileChooser.APPROVE_OPTION) { + return false; + } + File opFile = chooser.getSelectedFile(); + opFile = PamFileFilter.checkFileEnd(opFile, mapFileEnd, true); + // write it. + try { + ObjectOutputStream op = new ObjectOutputStream(new FileOutputStream(opFile)); + op.writeObject(getSettingsReference()); + op.close(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } + + + return true; + } + + /** + * Get a file chooser, which will remember folders, etc. while PAMGuard is open + * @return file chooser. + */ + private JFileChooser getFileChooser() { + if (ioFileChooser != null) { + return ioFileChooser; + } + PamFileFilter fileFilter = new PamFileFilter("Species map files", mapFileEnd); + ioFileChooser = new JFileChooser(); + ioFileChooser.setFileFilter(fileFilter); + ioFileChooser.setCurrentDirectory(new File(PamFolders.getDefaultProjectFolder())); + return ioFileChooser; + } + + /** + * Import global species maps from selected file. + * @param parentFrame + * @return + */ + public boolean importSpeciesMaps(Window parentFrame) { + JFileChooser chooser = getFileChooser(); + int ans = chooser.showOpenDialog(parentFrame); + if (ans != JFileChooser.APPROVE_OPTION) { + return false; + } + File ipFile = chooser.getSelectedFile(); + ipFile = PamFileFilter.checkFileEnd(ipFile, mapFileEnd, true); + GlobalSpeciesMap readSpeciesMap = null; + // read it. + try { + ObjectInputStream ip = new ObjectInputStream(new FileInputStream(ipFile)); + readSpeciesMap = (GlobalSpeciesMap) ip.readObject(); + } catch (IOException e) { + e.printStackTrace(); + return false; + } catch (ClassNotFoundException e) { + e.printStackTrace(); + return false; + } catch (Exception e) { + e.printStackTrace(); + return false; + } + + return handleNewSpeciesMap(readSpeciesMap); + } + + private boolean handleNewSpeciesMap(GlobalSpeciesMap readSpeciesMap) { + if (readSpeciesMap == null) { + return false; + } + // could put in a dialog to only select parts of the map if we wanted to ? + int ans = WarnOnce.showWarning("Global Species Map", + "Do you want to overwrite ALL PAMGaurd species maps with the imported data ?", + WarnOnce.YES_NO_OPTION); + if (ans == WarnOnce.CANCEL_OPTION) { + return false; + } + globalSpeciesMap = readSpeciesMap; + // no wupdate all datablock maps since they keep their own copies. + ArrayList allDatablocks = PamController.getInstance().getDataBlocks(); + for (PamDataBlock aBlock : allDatablocks) { + DataBlockSpeciesManager spManager = aBlock.getDatablockSpeciesManager(); + if (spManager == null) { + continue; + } + DataBlockSpeciesMap blockMap = globalSpeciesMap.get(aBlock); + if (blockMap != null) { + spManager.setDatablockSpeciesMap(blockMap); + } + } + + return true; + } } diff --git a/src/tethys/species/swing/DataBlockSpeciesDialog.java b/src/tethys/species/swing/DataBlockSpeciesDialog.java index d0c44791..adb84646 100644 --- a/src/tethys/species/swing/DataBlockSpeciesDialog.java +++ b/src/tethys/species/swing/DataBlockSpeciesDialog.java @@ -1,17 +1,20 @@ package tethys.species.swing; import java.awt.BorderLayout; +import java.awt.FlowLayout; import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import javax.swing.JButton; import javax.swing.JPanel; +import javax.swing.border.TitledBorder; import PamController.PamController; import PamView.PamGui; import PamView.dialog.PamDialog; import PamguardMVC.PamDataBlock; +import tethys.species.SpeciesMapManager; public class DataBlockSpeciesDialog extends PamDialog { @@ -24,6 +27,7 @@ public class DataBlockSpeciesDialog extends PamDialog { JPanel mainPanel = new JPanel(new BorderLayout()); speciesPanel = new DataBlockSpeciesPanel(dataBlock); mainPanel.add(BorderLayout.CENTER, speciesPanel.getDialogComponent()); + JButton itisButton = new JButton("Go to ITIS web site"); itisButton.setToolTipText("Go to ITIS website to search for species codes"); itisButton.addActionListener(new ActionListener() { @@ -33,9 +37,21 @@ public class DataBlockSpeciesDialog extends PamDialog { } }); JPanel nPanel = new JPanel(new BorderLayout()); + nPanel.setBorder(new TitledBorder("Code management")); nPanel.add(BorderLayout.EAST, itisButton); +// JPanel nwBit = new JPanel(new FlowLayout()); +// JButton exportButton = new JButton("Export"); +// exportButton.addActionListener(SpeciesMapManager.getInstance().getExportAction(parentFrame)); +// nwBit.add(exportButton); +// JButton importButton = new JButton("Import"); +// importButton.addActionListener(SpeciesMapManager.getInstance().getImportAction(parentFrame)); +// nwBit.add(importButton); +// nPanel.add(BorderLayout.WEST, nwBit); + + mainPanel.add(BorderLayout.NORTH, nPanel); setDialogComponent(mainPanel); + setResizable(true); } protected void gotoITIS() { diff --git a/src/tethys/species/swing/DataBlockSpeciesPanel.java b/src/tethys/species/swing/DataBlockSpeciesPanel.java index e2649f74..075cb080 100644 --- a/src/tethys/species/swing/DataBlockSpeciesPanel.java +++ b/src/tethys/species/swing/DataBlockSpeciesPanel.java @@ -6,6 +6,7 @@ import java.util.ArrayList; import javax.swing.BoxLayout; import javax.swing.JComponent; import javax.swing.JPanel; +import javax.swing.JScrollPane; import javax.swing.border.TitledBorder; import PamView.dialog.PamDialogPanel; @@ -30,7 +31,8 @@ public class DataBlockSpeciesPanel implements PamDialogPanel { this.dataBlock = dataBlock; mainPanel = new JPanel(new BorderLayout()); speciesPanel = new JPanel(); - mainPanel.add(speciesPanel, BorderLayout.CENTER); + JScrollPane scrollPane = new JScrollPane(speciesPanel, JScrollPane.VERTICAL_SCROLLBAR_AS_NEEDED, JScrollPane.HORIZONTAL_SCROLLBAR_NEVER); + mainPanel.add(scrollPane, BorderLayout.CENTER); mainPanel.setBorder(new TitledBorder(dataBlock.getDataName())); } diff --git a/src/tethys/swing/DetectionsExportPanel.java b/src/tethys/swing/DetectionsExportPanel.java index b790e7f8..6598f68e 100644 --- a/src/tethys/swing/DetectionsExportPanel.java +++ b/src/tethys/swing/DetectionsExportPanel.java @@ -11,10 +11,13 @@ import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.border.TitledBorder; +import PamController.PamController; +import PamView.dialog.PamDialog; import PamView.dialog.PamGridBagContraints; import PamView.panel.PamAlignmentPanel; import PamguardMVC.PamDataBlock; import tethys.TethysControl; +import tethys.species.DataBlockSpeciesManager; import tethys.swing.export.DetectionsExportWizard; public class DetectionsExportPanel extends TethysGUIPanel implements StreamTableObserver { @@ -53,6 +56,20 @@ public class DetectionsExportPanel extends TethysGUIPanel implements StreamTable if (selectedDataBlock == null) { return; } + + /** + * Check the species map is OK before doing anything. + */ + DataBlockSpeciesManager spManager = selectedDataBlock.getDatablockSpeciesManager(); + if (spManager != null) { + String error = spManager.checkSpeciesMapError(); + if (error != null) { + PamDialog.showWarning(PamController.getMainFrame(), "Datablock species manager error", error); + spManager.showSpeciesDialog(); + return; + } + } + DetectionsExportWizard.showDialog(getTethysControl().getGuiFrame(), getTethysControl(), selectedDataBlock); }