diff --git a/.classpath b/.classpath index 49faf461..924124d8 100644 --- a/.classpath +++ b/.classpath @@ -6,7 +6,7 @@ - + diff --git a/.gitignore b/.gitignore index 6254b898..1cbbf601 100644 --- a/.gitignore +++ b/.gitignore @@ -111,3 +111,4 @@ settings.xml .classpath .classpath .settings/org.eclipse.jdt.core.prefs +.classpath diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs index 51bb81c3..a474f512 100644 --- a/.settings/org.eclipse.core.resources.prefs +++ b/.settings/org.eclipse.core.resources.prefs @@ -1,5 +1,6 @@ eclipse.preferences.version=1 encoding//src/rawDeepLearningClassifer/segmenter/SegmenterProcess.java=UTF-8 +encoding//src/test=UTF-8 encoding//src/test/resources=UTF-8 encoding/=UTF-8 encoding/src=UTF-8 diff --git a/.settings/org.eclipse.jdt.core.prefs b/.settings/org.eclipse.jdt.core.prefs index ecb498c8..43939db6 100644 --- a/.settings/org.eclipse.jdt.core.prefs +++ b/.settings/org.eclipse.jdt.core.prefs @@ -1,9 +1,9 @@ eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=17 +org.eclipse.jdt.core.compiler.codegen.targetPlatform=21 org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=17 +org.eclipse.jdt.core.compiler.compliance=21 org.eclipse.jdt.core.compiler.debug.lineNumber=generate org.eclipse.jdt.core.compiler.debug.localVariable=generate org.eclipse.jdt.core.compiler.debug.sourceFile=generate @@ -13,4 +13,4 @@ org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning org.eclipse.jdt.core.compiler.release=enabled -org.eclipse.jdt.core.compiler.source=17 +org.eclipse.jdt.core.compiler.source=21 diff --git a/dependency-reduced-pom.xml b/dependency-reduced-pom.xml index 02e9f7ce..c3db71b0 100644 --- a/dependency-reduced-pom.xml +++ b/dependency-reduced-pom.xml @@ -107,6 +107,7 @@ META-INF/*.SF META-INF/*.DSA META-INF/*.RSA + test/resources/** diff --git a/lib64/SailDaqJNI.dll b/lib64/SailDaqJNI.dll index 7da8ea4d..604bac0e 100644 Binary files a/lib64/SailDaqJNI.dll and b/lib64/SailDaqJNI.dll differ diff --git a/pom.xml b/pom.xml index 5706741b..7f5eb4e8 100644 --- a/pom.xml +++ b/pom.xml @@ -4,7 +4,7 @@ 4.0.0 org.pamguard Pamguard - 2.02.12 + 2.02.11d Pamguard Pamguard using Maven to control dependencies www.pamguard.org @@ -100,8 +100,8 @@ META-INF/*.SF META-INF/*.DSA - META-INF/*.RSA - test/resources/** + META-INF/*.RSA + test/resources/** @@ -180,25 +180,7 @@ --> - - - - + @@ -598,7 +580,7 @@ com.fazecast jSerialComm - 2.11.0 + 2.5.3 diff --git a/readme.md b/readme.md index 721b10db..886f0319 100644 --- a/readme.md +++ b/readme.md @@ -1,26 +1,28 @@ # PAMGuard + +## Introduction PAMGuard is a bioacoustics analysis program designed for use in real time research contexts and for the processing of large datasets. PAMGuard provides users access to a suite of state-of-the-art auotmated analysis algorithms alongside displays for visualisation data and a comprehensive data management systems. -# Why do we need PAMGuard? +## Why do we need PAMGuard? PAMGuard fufills two main requirements within marine bioacoustics -1) **Real time operation** - Almost all PAMGuard features and modules work in real time - this allows scientists and industry to detect, classify and loclaise animals in real time on a standard consumer laptop, enabling mitigations and research survey without expensive bespoke software solutions and the transparncy of open source software. +1) **Real time operation**: Almost all PAMGuard features and modules work in real time - this allows scientists and industry to detect, classify and localise the position of animals in real time on a standard consumer laptop, enabling mitigation and research surveys without expensive bespoke software solutions and the transparncy of open source software. -2) **Processing and visuslisation of large datasets** - +2) **Processing and visuslisation of large datasets**: Off-the-shelf autonomous PAM devices, large scale surveys involving drifters, towed arrays, glidders, bottom mounted devices and real time continuous monitoring system all generate huge volumes of data whcih requires automated analysis approaches. PAMGuard allows the processing of very large passive acoustic datasets using automated algorothms and crucially provides comprehensive visualisation tools for a manual analyst to check the results. - ## Installation -PAMGuard is available on Windows and can be downloaded from the [PAMGuard website](www.pamguard.org). Note that we are considering MacOS installers but they are not available at this time. +PAMGuard is available on Windows and can be downloaded from the [PAMGuard website](www.pamguard.org). Note that we are considering MacOS and Linux installers but they are not available at this time. -## Tutorial +## Quick start tutorial PAMGuard is a modular program with two modes; real-time and viewer. Typically a user will start with real-time model, either in the field collecting data or post processing sound files from a recorder. Once data are processed, users move on to viewer mode where data can be explored and further processed. -Upon opening PAMGuard for the first time you are greeted with a blank screen. You must add a series of modules to create the desired acosutic workflow. For example if processing sound files then first add the Sound Acquisition module **_File->Add Modules->Sound Processing->Sound Acquisition_**. Then add the desired detection algorothms e.g. **_File->Add Modules->Detector->Click Detectors_**. Some modules (such as the click detector) have their own displays, others are added to more generalised displays. For example, the whislte and moan detector module shows detections on a spectrgram display. First add a new tab using **_File->Add Modules->Displays->User Display**. Click on the user display tab and then from the top menu select **_User display-> New Spectrgram_**. Right click on the added spectrgram and select whistle and moan contours to show whistle detections overlaid on the raw spectrgram. +Upon opening PAMGuard for the first time you are greeted with a blank screen. You must add a series of modules to create the desired acosutic workflow - this is referred to as PAMGuard *data model*. For example if processing sound files then first add the Sound Acquisition module **_File->Add Modules->Sound Processing->Sound Acquisition_**. Then add the desired detection algorothms e.g. **_File->Add Modules->Detector->Click Detectors_**. Some modules (such as the click detector) have their own bespoke displays, others are added to more generalised displays. For example, the whistle and moan detector module shows detections on a spectrgram display, time base display, map etc.. First add a new tab using **_File->Add Modules->Displays->User Display**. Click on the user display tab and then from the top menu select **_User display-> New Spectrogram_**. Right click on the added spectrgram and select whistle and moan contours to show whistle detections overlaid on the raw spectrgram. -Make sure to add the database and binary file storage modules **_File->Add Modules->Utilities->..._**) to save data then press the run button (red button) and data will process. PAMGuard can handle huge datasets so runing might take hours or even days. Progress is shown on the bottom of the screen. +Make sure to add the database and binary file storage modules **_File->Add Modules->Utilities->..._**) to save data then press the run button (red button) and data will process. PAMGuard can handle huge datasets so running might take hours or even days. Progress is shown on the bottom of the screen. + +Once the data has run, open PAMGuard viewer mode. Select the database you used to process the data along and thebianry file storage path and PAMGuard will open, showing an overview of the dataset in a new _Data map_ tab. Right click anywhere on the data map and select "Center data here" - PAMGuard will load the data for the selected period which can be explored using whichever displays have been added to the data model. ## Features - ### Hardware integration PAMGuard connects with hardware such as various GPS and AIS systems and a multitude of different sound cards (e.g. [National Instruments](www.ni.com) devices, [SAIL DAQ cards](www.smruconsulting.com/contact-us), almost all ASIO sound cards and standard computer sound cards) for real time data collection and processing. PAMGuard also works with some very bespoke hardware such as [DIFAR Sonobuoys](); @@ -36,31 +38,35 @@ PAMGuard is designed to collect/process data from large acosutic datasets. PAMGu ### Access to detection and classification algorithms PAMGuard allows users to inegrate automated detection and classification algorithms directly into their acosutic workflow. There are a multitude of differwent algorothms to choose from, including a basic click detector, whislte and moan detector, GPL detector, click train detectors and many others. The idea behind PAMGuard is allow researchers to access open source state-of-the-art algorithms devleoped within the scientific community - if you want to contribute and get your algorithm into PAMGuard get in touch. -###Localisation +### Localisation PAMGuard has a mutltude of different options for acoustic loclaisation. There's a comprehesnive beam forming module for beam forming arrays, a large aperture localiser for 3D loclaisation and target motion analysis for towed hydrophone arrays. -###Soundscape analysis +### Soundscape analysis PAMGuard has a noise band (which supports third octave noise bands) and long term spectral average module for soundscape analysis. ### GIS Almsot all detection data can be visualised on a map. PAMGaurd also supports plotting GPS and AIS data. ### Suite of data visualisation tools -An important aspect of PAMGuard is the ability for users to explore porcessed data. This is +An important aspect of PAMGuard is the ability for users to explore porcessed data. PAMGuard allows users to visualise data at multiple different times scales, from inspecting individual waveforms microseconds long to datagrams showing detector output or soundscape metrics over days, weeks or even years. ### Advanced manual annotation -The displays within PAMGuard support a variety of manual annottion tools. A simple spectrogram +The displays within PAMGuard support a variety of manual annotation tools. Users can add data to annotations in multiple ways, from simple text annotations to complex forms created by users. ### Deep learning integration +PAMGuard allows users to run their own deep learning models using the deep learning module. AI can therfore be integrated into PAMGuard workflows, allowing for more efficient analysis of data. -### Meatadata standard and Tethys compatibility +### Metadata standard and Tethys compatibility +PAMGuard Integrates with Tethys database. Users can export processed PAMGuard data to a Tethys database seamlessly; this ifeature is great for large scale projects or organisatiosn with long term datasets. ## Feature roadmap There's lots of features we would like to add to PAMGuard. If you want to add a feature you can either code it up yourself in Java and submit a pull request or get in touch with us to discuss how to it might be integrated. Some smaller features might be in our roadmap anyway but larger features usually require funding. Some features we are thinking about (but do not necassarily have time for yet) are; * Support for decidecade noise bands (base 10 filter bank) in noise band monitor to meet Euopean standards -* Capabaility to export data directly from PAMGaurd e.g. as MAT files. -* Automated test suite to make releases more stable. Note that unit and integration tests are also being slowly incorporated. +* Capabaility to export data directly from PAMGaurd e.g. as MAT files (in progress). +* Automated test suite to make releases more stable. Note that unit and integration tests are also being slowly incorporated. +* Support for ARM based computers (in progress). +* A graphical user interface and Python library for training PAMGuard compatible deep learning models. ## Development This is the main code repository for the PAMGuard software and was created on 7 January 2022 from a [sourceforge SVN repository](https://sourceforge.net/p/pamguard/svn/HEAD/tree/) revision r6278. diff --git a/src/Acquisition/FileInputSystem.java b/src/Acquisition/FileInputSystem.java index 9b99ad3b..f3988d61 100644 --- a/src/Acquisition/FileInputSystem.java +++ b/src/Acquisition/FileInputSystem.java @@ -181,7 +181,6 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe this.acquisitionDialog = acquisitionDialog; - return getDialogPanel(); } @@ -198,7 +197,7 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe protected JPanel createDaqDialogPanel() { - JPanel p = new JPanel(); + PamPanel p = new PamPanel(); p.setBorder(new TitledBorder("Select sound file")); GridBagLayout layout = new GridBagLayout(); layout.columnWidths = new int[]{100, 100, 10}; @@ -253,6 +252,7 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe // constraints.gridwidth = 2; // addComponent(p, fileDateText = new JTextField(), constraints); // fileDateText.setEnabled(false); + return p; } diff --git a/src/Acquisition/FolderInputSystem.java b/src/Acquisition/FolderInputSystem.java index c8c2d872..7f777aa1 100644 --- a/src/Acquisition/FolderInputSystem.java +++ b/src/Acquisition/FolderInputSystem.java @@ -32,6 +32,7 @@ import javafx.application.Platform; import pamguard.GlobalArguments; import Acquisition.pamAudio.PamAudioFileManager; import Acquisition.pamAudio.PamAudioFileFilter; +import Acquisition.pamAudio.PamAudioFileLoader; import Acquisition.pamAudio.PamAudioSystem; import PamController.DataInputStore; import PamController.InputStoreInfo; @@ -92,6 +93,12 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D * Text field for skipping initial few seconds of a file. */ private JTextField skipSecondsField; + + /** + * Panel which shows bespoke settings for certain audio loaders. Contains nothing + * if the audio loader has no settings or no file is selected. + */ + protected PamPanel audioLoaderHolder; @Override public boolean runFileAnalysis() { @@ -170,6 +177,8 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D } } + + @Override protected JPanel createDaqDialogPanel() { JPanel p = new JPanel(); @@ -228,6 +237,7 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D constraints.gridwidth = 2; constraints.fill = GridBagConstraints.NONE; constraints.anchor = GridBagConstraints.WEST; + addComponent(p, mergeFiles = new JCheckBox("Merge contiguous files"), constraints); if (PamController.getInstance().getRunMode() == PamController.RUN_PAMVIEW) { constraints.gridx+=2; @@ -236,18 +246,28 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D checkFiles.addActionListener(new CheckFiles()); } -// if (SMRUEnable.isEnable()) { + // if (SMRUEnable.isEnable()) { // no reason to hide this option from users. - constraints.gridy++; - constraints.gridx = 0; - constraints.gridwidth = 1; - addComponent(p, new JLabel("Skip initial :"), constraints); - constraints.gridx++; - addComponent(p, skipSecondsField = new JTextField(4), constraints); - constraints.gridx++; - addComponent(p, new JLabel("seconds"), constraints); - constraints.anchor = GridBagConstraints.EAST; -// } + constraints.gridy++; + constraints.gridx = 0; + constraints.gridwidth = 1; + addComponent(p, new JLabel("Skip initial :"), constraints); + constraints.gridx++; + addComponent(p, skipSecondsField = new JTextField(4), constraints); + constraints.gridx++; + addComponent(p, new JLabel("seconds"), constraints); + // } + + + //panel to show bespoke settings for certain audio loaders. + constraints.anchor = GridBagConstraints.WEST; + constraints.gridx = 0; + constraints.gridy++; + constraints.gridwidth = 3; + addComponent(p, audioLoaderHolder = new PamPanel(), constraints); + + GridBagLayout layout2 = new GridBagLayout(); + audioLoaderHolder.setLayout(layout2); return p; } @@ -525,10 +545,14 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D List asList = allFiles; setSelectedFileTypes(acquisitionControl.soundFileTypes.getUsedTypes(allFiles)); + //set the date of the first file. setFileDateText(); + + //set any bespoke options for the files to be laoded. + setFileOptionPanel(); + // also open up the first file and get the sample rate and number of channels from it // and set these - File file = getCurrentFile(); if (file == null) return; AudioInputStream audioStream; @@ -558,7 +582,7 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D /****FX GUI stuff****/ if (folderInputPane!=null) { Platform.runLater(()->{ - folderInputPane.newFileList(fileListData); + folderInputPane.newFileList(fileListData); }); } } @@ -572,7 +596,39 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D protected int fudgeNumChannels(int nChannels) { return nChannels; } + + /** + * Set bespoke options for certain file types. + */ + public void setFileOptionPanel() { + getDialogPanel(); // make sure panel is created + audioLoaderHolder.removeAll(); + + if (allFiles.size() > 0) { + //Get all the audio file laoders that will be used for this list of files. Usually + //just one but possible that there can be mixed files. + ArrayList loaders = PamAudioFileManager.getInstance().getAudioFileLoaders(allFiles); + + GridBagConstraints constraints = new GridBagConstraints(); + constraints.gridx = 0; + constraints.gridy = 0; + for (PamAudioFileLoader loader : loaders) { + if (loader.getSettingsPane()!=null) { +// System.out.println("ADD AUDIO PANEL: " +loader.getSettingsPane().getAudioLoaderPanel()); + //gridbag layout + addComponent(audioLoaderHolder, loader.getSettingsPane().getAudioLoaderPanel(), constraints); + loader.getSettingsPane().setParams(); + constraints.gridy++; + } + } + } + + } + + /** + * Show the date of the first file in the dialog. + */ public void setFileDateText() { if (allFiles.size() > 0) { long fileTime = getFileStartTime(getCurrentFile()); @@ -768,6 +824,17 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D return false; } } + + //get bespoke paramters from selected audio loaders. + ArrayList loaders = PamAudioFileManager.getInstance().getAudioFileLoaders(allFiles); + + + for (PamAudioFileLoader loader : loaders) { + if (loader.getSettingsPane()!=null) { + loader.getSettingsPane().getParams(); + } + } + return super.dialogGetParams(); } @@ -786,6 +853,7 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D if (skipSecondsField!=null) { skipSecondsField.setText(String.format("%.1f", fileInputParameters.skipStartFileTime/1000.)); } + } @Override @@ -950,6 +1018,8 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D String bs = String.format("%d,%d,%d,%s", nFiles,currentFile,generalStatus,currFile); return bs; } + + } diff --git a/src/Acquisition/layoutFX/AcquisitionPaneFX.java b/src/Acquisition/layoutFX/AcquisitionPaneFX.java index 59400599..14fc557e 100644 --- a/src/Acquisition/layoutFX/AcquisitionPaneFX.java +++ b/src/Acquisition/layoutFX/AcquisitionPaneFX.java @@ -6,23 +6,18 @@ import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.geometry.Insets; import javafx.geometry.Orientation; -import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.control.TextField; import javafx.scene.layout.HBox; import javafx.scene.layout.Pane; import javafx.scene.layout.Priority; -import javafx.scene.paint.Color; import javafx.stage.Stage; import javafx.util.StringConverter; import pamViewFX.PamGuiManagerFX; -import pamViewFX.fxGlyphs.PamGlyphDude; import pamViewFX.fxNodes.PamBorderPane; -import pamViewFX.fxNodes.PamButton; import pamViewFX.fxNodes.PamComboBox; import pamViewFX.fxNodes.PamGridPane; -import pamViewFX.fxNodes.PamHBox; import pamViewFX.fxNodes.PamTextField; import pamViewFX.fxNodes.PamVBox; import pamViewFX.fxNodes.flipPane.FlipPane; @@ -43,7 +38,7 @@ import dataMap.filemaps.OfflineFileParameters; */ public class AcquisitionPaneFX extends SettingsPane{ - private final static int TEXT_FIELD_WIDTH = 60; + private final static int TEXT_FIELD_WIDTH = 80; /** * Reference to acquisition control. @@ -120,13 +115,13 @@ public class AcquisitionPaneFX extends SettingsPane{ /** * */ - private FlipPane flipPane; + private PamFlipPane flipPane; /** * Pane which can be used for advanced settings. */ - private PamBorderPane advancedSettingPane; +// private PamBorderPane advancedSettingPane; /** * Title label for the advanced pane. @@ -147,12 +142,13 @@ public class AcquisitionPaneFX extends SettingsPane{ super(null); mainPane = new PamBorderPane(); mainPane.setPrefWidth(400); + mainPane.setPadding(new Insets(5,5,5,5)); this.acquisitionControl=aquisitionControl; this.acquisitionParameters=acquisitionControl.getAcquisitionParameters(); //create the flip pane. - flipPane=new FlipPane(); + flipPane=new PamFlipPane(); flipPane.setFlipDirection(Orientation.HORIZONTAL); flipPane.setFlipTime(PamFlipPane.FLIP_TIME); //default is 700ms- way too high //flipPane.prefWidthProperty().bind(mainPane.widthProperty()); @@ -166,45 +162,45 @@ public class AcquisitionPaneFX extends SettingsPane{ flipPane.getFront().getChildren().add(mainPane); //create the advanced flip pane. - advancedSettingPane = createAdvSettingsPane(); - flipPane.getBack().getChildren().add(advancedSettingPane); +// advancedSettingPane = createAdvSettingsPane(); +// flipPane.getBack().getChildren().add(advancedSettingPane); //System.out.println("MAKE PANE: " + acquisitionParameters.getDaqSystemType()); } - /** - * Create the advanced settings pane which can be accessed by DAQ panes if needed. - */ - private PamBorderPane createAdvSettingsPane() { - - PamButton back = new PamButton(); - back.setGraphic(PamGlyphDude.createPamIcon("mdi2c-chevron-left", Color.WHITE, PamGuiManagerFX.iconSize)); - - back.setOnAction((action)->{ - flipPane.flipToFront(); - }); - - PamBorderPane advPane = new PamBorderPane(); - advPane.setPadding(new Insets(5,5,5,5)); - - PamHBox buttonHolder = new PamHBox(); - - buttonHolder.setBackground(null); - //buttonHolder.setStyle("-fx-background-color: red;"); - buttonHolder.setAlignment(Pos.CENTER_LEFT); - buttonHolder.getChildren().addAll(back, advLabel = new Label("Adv. Settings")); - advLabel.setAlignment(Pos.CENTER); - advLabel.setMaxWidth(Double.MAX_VALUE); //need to make sure label is in center. - PamGuiManagerFX.titleFont2style(advLabel); - - advLabel.setAlignment(Pos.CENTER); - HBox.setHgrow(advLabel, Priority.ALWAYS); - - advPane.setTop(buttonHolder); - - return advPane; - - } +// /** +// * Create the advanced settings pane which can be accessed by DAQ panes if needed. +// */ +// private PamBorderPane createAdvSettingsPane() { +// +// PamButton back = new PamButton(); +// back.setGraphic(PamGlyphDude.createPamIcon("mdi2c-chevron-left", Color.WHITE, PamGuiManagerFX.iconSize)); +// +// back.setOnAction((action)->{ +// flipPane.flipToFront(); +// }); +// +// PamBorderPane advPane = new PamBorderPane(); +// advPane.setPadding(new Insets(5,5,5,5)); +// +// PamHBox buttonHolder = new PamHBox(); +// +// buttonHolder.setBackground(null); +// //buttonHolder.setStyle("-fx-background-color: red;"); +// buttonHolder.setAlignment(Pos.CENTER_LEFT); +// buttonHolder.getChildren().addAll(back, advLabel = new Label("Adv. Settings")); +// advLabel.setAlignment(Pos.CENTER); +// advLabel.setMaxWidth(Double.MAX_VALUE); //need to make sure label is in center. +// PamGuiManagerFX.titleFont2style(advLabel); +// +// advLabel.setAlignment(Pos.CENTER); +// HBox.setHgrow(advLabel, Priority.ALWAYS); +// +// advPane.setTop(buttonHolder); +// +// return advPane; +// +// } /** * Create the Sound Aquisition pane for real time monitoring. @@ -606,11 +602,12 @@ public class AcquisitionPaneFX extends SettingsPane{ } public PamBorderPane getAdvancedPane() { - return this.advancedSettingPane; + return this.flipPane.getAdvContentPane() +; } - public Label getAdvancedLabel() { - return this.advLabel; + public TextField getAdvancedLabel() { + return this.flipPane.getAdvLabel(); } } diff --git a/src/Acquisition/layoutFX/FolderInputPane.java b/src/Acquisition/layoutFX/FolderInputPane.java index cf0b0e3e..5a6d2366 100644 --- a/src/Acquisition/layoutFX/FolderInputPane.java +++ b/src/Acquisition/layoutFX/FolderInputPane.java @@ -1,5 +1,6 @@ package Acquisition.layoutFX; +import java.awt.GridBagConstraints; import java.io.File; import java.util.ArrayList; import java.util.List; @@ -12,6 +13,8 @@ import Acquisition.FileInputParameters; import Acquisition.FolderInputParameters; import Acquisition.FolderInputSystem; import Acquisition.pamAudio.PamAudioFileFilter; +import Acquisition.pamAudio.PamAudioFileLoader; +import Acquisition.pamAudio.PamAudioFileManager; import PamController.PamController; import PamController.PamFolders; import PamUtils.PamCalendar; @@ -48,7 +51,7 @@ import pamViewFX.fxNodes.PamProgressBar; import pamViewFX.fxNodes.PamVBox; /** - * Pane for the folder input of the sound acquisition. + * JavaFX pane for the folder input of the sound acquisition. * * @author Jamie Macaulay * @@ -147,6 +150,8 @@ public class FolderInputPane extends DAQSettingsPane{ */ private ToggleButton mergeContigious; + private PamBorderPane audioHolderloader; + // /** // * The folder input system. // * @param folderInputSystem - the folder system. @@ -245,8 +250,11 @@ public class FolderInputPane extends DAQSettingsPane{ Label utilsLabel=new Label("Sound file utilities"); PamGuiManagerFX.titleFont2style(utilsLabel); + // + audioHolderloader = new PamBorderPane(); + pamVBox.getChildren().addAll(fileSelectBox, subFolderPane, progressBar, createTablePane(), - fileDateText=new Label(), utilsLabel, createUtilsPane()); + fileDateText=new Label(), audioHolderloader, utilsLabel, createUtilsPane()); //allow users to check file headers in viewer mode. // if (PamController.getInstance().getRunMode() == PamController.RUN_PAMVIEW) { @@ -361,6 +369,7 @@ public class FolderInputPane extends DAQSettingsPane{ return new PamBorderPane(table); } + /** * Open a dialog to select either a folder or a list of files. * @param folderDir - true to use directory chooser, false to use multiple file chooser. @@ -594,6 +603,9 @@ public class FolderInputPane extends DAQSettingsPane{ this.table.getItems().addAll(fileList); fileDateStrip.setFileList(fileList); + + //set any bespoke options for the files to be laoded. + setFileOptionPane(fileList); //need to set the sample rate and channels in the main pane. if (fileList!=null && fileList.size()>0) { @@ -609,6 +621,34 @@ public class FolderInputPane extends DAQSettingsPane{ // fileDateStrip.setDate(folderInputSystem.getFileStartTime(selFiles[0])); // } } + + + /** + * Set bespoke options for certain file types. + */ + public void setFileOptionPane(ObservableList fileList) { + + audioHolderloader.setCenter(null); + + if (fileList.size() > 0) { + //Get all the audio file laoders that will be used for this list of files. Usually + //just one but possible that there can be mixed files. + ArrayList loaders = PamAudioFileManager.getInstance().getAudioFileLoaders(fileList); + + PamVBox vBox = new PamVBox(); + vBox.setSpacing(5); + for (PamAudioFileLoader loader : loaders) { + if (loader.getSettingsPane()!=null) { + //add the settings pane to the vbox + vBox.getChildren().add(loader.getSettingsPane().getAudioLoaderPane()); + loader.getSettingsPane().setParams(); + } + } + audioHolderloader.setCenter(vBox); + + } + + } /** * Called whenever a new file worker is initialised to search a folder for wav files. @@ -637,16 +677,30 @@ public class FolderInputPane extends DAQSettingsPane{ public void setParams() { //set the parameters for the dialog. this.setParams(folderInputSystem.getFolderInputParameters()); + + } @Override public boolean getParams() { FolderInputParameters params = this.getParams(folderInputSystem.getFolderInputParameters()); + + //get bespoke paramters from selected audio loaders. Note these are global and so are not part + //of the folder input system + ArrayList loaders = PamAudioFileManager.getInstance().getAudioFileLoaders(); + + for (PamAudioFileLoader loader : loaders) { + if (loader.getSettingsPane()!=null) { + loader.getSettingsPane().getParams(); + } + } + if (params == null) return false; else { this.folderInputSystem.setFolderInputParameters(params); return true; } + } diff --git a/src/Acquisition/offlineFuncs/OfflineWavFileServer.java b/src/Acquisition/offlineFuncs/OfflineWavFileServer.java index b10b27dc..582c4bdc 100644 --- a/src/Acquisition/offlineFuncs/OfflineWavFileServer.java +++ b/src/Acquisition/offlineFuncs/OfflineWavFileServer.java @@ -178,7 +178,7 @@ public class OfflineWavFileServer extends OfflineFileServer { return false; } - PamAudioFileLoader audioFile = PamAudioFileManager.getInstance().getAudioLoader(mapPoint.getSoundFile()); + PamAudioFileLoader audioFile = PamAudioFileManager.getInstance().getAudioFileLoader(mapPoint.getSoundFile()); if (audioFile==null) { System.err.println("OfflineWavFileServer: could not find audio loader for mapped sound file: " + mapPoint.getSoundFile()); diff --git a/src/Acquisition/pamAudio/FlacAudioFile.java b/src/Acquisition/pamAudio/FlacAudioFile.java index 13d95b87..0330ef3d 100644 --- a/src/Acquisition/pamAudio/FlacAudioFile.java +++ b/src/Acquisition/pamAudio/FlacAudioFile.java @@ -233,4 +233,10 @@ public class FlacAudioFile implements PamAudioFileLoader { return (end.equals(".flac")); } + @Override + public PamAudioSettingsPane getSettingsPane() { + // TODO Auto-generated method stub + return null; + } + } diff --git a/src/Acquisition/pamAudio/PamAudioFileLoader.java b/src/Acquisition/pamAudio/PamAudioFileLoader.java index 90cc524d..797ab56d 100644 --- a/src/Acquisition/pamAudio/PamAudioFileLoader.java +++ b/src/Acquisition/pamAudio/PamAudioFileLoader.java @@ -48,5 +48,12 @@ public interface PamAudioFileLoader { public boolean loadAudioData(OfflineFileServer offlineFileServer, PamDataBlock dataBlock, OfflineDataLoadInfo offlineDataLoadInfo, ViewLoadObserver loadObserver); + /** + * Get a settings pane for the audio loader + * @return settings pane for audio loader - can be null. + */ + public PamAudioSettingsPane getSettingsPane(); + + } diff --git a/src/Acquisition/pamAudio/PamAudioFileManager.java b/src/Acquisition/pamAudio/PamAudioFileManager.java index bf13f904..aa321e30 100644 --- a/src/Acquisition/pamAudio/PamAudioFileManager.java +++ b/src/Acquisition/pamAudio/PamAudioFileManager.java @@ -3,12 +3,15 @@ package Acquisition.pamAudio; import java.io.File; import java.io.IOException; import java.util.ArrayList; +import java.util.List; import javax.sound.sampled.AudioInputStream; import javax.sound.sampled.UnsupportedAudioFileException; import org.codehaus.plexus.util.FileUtils; +import Acquisition.sud.SudAudioFile; + /** * Central class for opening sound files. *

@@ -56,7 +59,7 @@ public class PamAudioFileManager { * @param soundFile - the sound file * @return the audio file loader. */ - public PamAudioFileLoader getAudioLoader(File soundFile) { + public PamAudioFileLoader getAudioFileLoader(File soundFile) { for (int i = 0; i < pamAudioFileTypes.size(); i++) { if (isExtension(soundFile, pamAudioFileTypes.get(i))) { return pamAudioFileTypes.get(i); @@ -169,6 +172,23 @@ public class PamAudioFileManager { public ArrayList getAudioFileLoaders() { return this.pamAudioFileTypes; } + + /** + * Get the loaders which are needed to open a list of files + * @param files - the files to find audio loaders for. + * @return a list of the audio loaders required for the file list + */ + public ArrayList getAudioFileLoaders(List files) { + ArrayList audioLoaders = new ArrayList(); + PamAudioFileLoader loader; + for (int i=0; i worker; + private volatile SudMapWorker sudMapWorker; + /** + * Settings pane to allow users to set some additional options. + */ + private SudAudioSettingsPane sudAudioSettingsPane; + + /** + * Parameters for the sud file. TODO Note: PamAudioManager is always a single + * instance referenced globally from PAMGuard. Having parameters is therefore + * slightly problematic because they will apply across SoundAcquisition modules. + * So in the case that someone is using two or more Sound Acquisition modules + * then selecting zero and non -zero pad would be impossible + */ + private PamSudParams sudParams = new PamSudParams(); + public SudAudioFile() { super(); fileExtensions = new ArrayList(Arrays.asList(new String[] { ".sud" })); + PamSettingManager.getInstance().registerSettings(this); } @Override @@ -63,7 +86,7 @@ public class SudAudioFile extends WavAudioFile { if (new File(soundFile.getAbsolutePath() + "x").exists()) { // System.out.println("----NO NEED TO MAP SUD FILE-----" + soundFile); try { - return new SudAudioFileReader().getAudioInputStream(soundFile); + return new SudAudioFileReader(sudParams.zeroPad).getAudioInputStream(soundFile); } catch (UnsupportedAudioFileException | IOException e) { // TODO Auto-generated catch block e.printStackTrace(); @@ -181,7 +204,7 @@ public class SudAudioFile extends WavAudioFile { // System.out.println("START OPEN SUD FILE:"); this.sudMapListener = new SudMapProgress(pamWorker); - stream = new SudAudioFileReader().getAudioInputStream(soundFile, sudMapListener); + stream = new SudAudioFileReader(sudParams.zeroPad).getAudioInputStream(soundFile, sudMapListener); // System.out.println("END SUD FILE:"); @@ -216,5 +239,51 @@ public class SudAudioFile extends WavAudioFile { } } + + @Override + public PamAudioSettingsPane getSettingsPane() { + if (sudAudioSettingsPane==null) { + sudAudioSettingsPane = new SudAudioSettingsPane(this); + } + return sudAudioSettingsPane; + } + + @Override + public String getUnitName() { + return "PamAudioManager"; + } + + @Override + public String getUnitType() { + return "sud_files"; + } + + @Override + public Serializable getSettingsReference() { + return sudParams; + } + + @Override + public long getSettingsVersion() { + return PamSudParams.serialVersionUID; + } + + + @Override + public boolean restoreSettings(PamControlledUnitSettings pamControlledUnitSettings) { + try { + sudParams = ((PamSudParams) pamControlledUnitSettings.getSettings()).clone(); + + return true; + } + catch (ClassCastException e) { + e.printStackTrace(); + } + return false; + } + + public PamSudParams getSudParams() { + return this.sudParams; + } } \ No newline at end of file diff --git a/src/Acquisition/pamAudio/SudAudioFileReader.java b/src/Acquisition/sud/SudAudioFileReader.java similarity index 76% rename from src/Acquisition/pamAudio/SudAudioFileReader.java rename to src/Acquisition/sud/SudAudioFileReader.java index b38ea34e..f69467f6 100644 --- a/src/Acquisition/pamAudio/SudAudioFileReader.java +++ b/src/Acquisition/sud/SudAudioFileReader.java @@ -1,4 +1,4 @@ -package Acquisition.pamAudio; +package Acquisition.sud; import java.io.File; import java.io.IOException; @@ -25,15 +25,32 @@ public class SudAudioFileReader { */ SudParams sudParams; - + /** + * Constructor to create an Sud Audio reader with a + * default true to zeropad sud files. + * */ public SudAudioFileReader() { + this(true); + } + + + /** + * Constructor to create an Sud Audio reader. Allows the option of zero padding. + * Zero padding fills gaps in sud files with zeros - these gaps are usually due + * to errors in the recording hardware.Without zero pad then time drift within a + * file can be difficult to predict, however zero padding means the sample + * numbers in other files e.g. csv sensor files will not align. + * + * @param zeroPad - true to zero pad sud files. + */ + public SudAudioFileReader(boolean zeroPad) { sudParams = new SudParams(); //set up the sud params for default. i.e. just read files and //don't save any decompressed or meta data. // sudParams.saveWav = false; // sudParams.saveMeta = false; sudParams.setFileSave(false, false, false, false); - sudParams.zeroPad = true; + sudParams.zeroPad = zeroPad; } /** diff --git a/src/Acquisition/sud/SudAudioSettingsPane.java b/src/Acquisition/sud/SudAudioSettingsPane.java new file mode 100644 index 00000000..f689bb34 --- /dev/null +++ b/src/Acquisition/sud/SudAudioSettingsPane.java @@ -0,0 +1,158 @@ +package Acquisition.sud; + +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; + +import javax.swing.JCheckBox; +import javax.swing.JLabel; +import javax.swing.JPanel; + +import Acquisition.pamAudio.PamAudioSettingsPane; +import PamView.dialog.PamGridBagContraints; +import PamView.panel.PamPanel; +import javafx.scene.control.Tooltip; +import javafx.scene.layout.Pane; +import pamViewFX.fxNodes.PamHBox; +import pamViewFX.fxNodes.utilityPanes.PamToggleSwitch; + +/** + * Some Swing and FX controls to allow a user to chnage sud parameters. + */ +public class SudAudioSettingsPane implements PamAudioSettingsPane { + + private SudAudioFile sudAudioFile; + + private SudSettingsPanel sudAudioPanel; + + private SudSettingsPane sudAudioPaneFX; + + private String sudTooltip = "Zero pad sud files. Zero padding replaces sections of sud files \n" + + "with corrupt or no data with zeros. This can improve time drift. "; + + + public SudAudioSettingsPane(SudAudioFile sudAudioFile) { + this.sudAudioFile=sudAudioFile; + } + + @Override + public Pane getAudioLoaderPane() { + if (sudAudioPaneFX==null) { + createSudAudioPaneFX(); + } + return sudAudioPaneFX; + } + + @Override + public PamPanel getAudioLoaderPanel() { + if (sudAudioPanel==null) { + createSudAudioPanel(); + } + return sudAudioPanel; + } + + private void createSudAudioPanel() { + sudAudioPanel = new SudSettingsPanel(); + } + + private void createSudAudioPaneFX() { + sudAudioPaneFX = new SudSettingsPane(); + } + + public void setParams(PamSudParams sudParams) { + //System.out.println("Set SUD PARAMS: " + sudParams + " " + sudParams.zeroPad); + if (sudAudioPanel!=null) sudAudioPanel.setParams(sudParams);; + if (sudAudioPaneFX!=null) sudAudioPaneFX.setParams(sudParams);; + + } + + public PamSudParams getParams(PamSudParams sudParams) { + //System.out.println("Get SUD PARAMS: " + sudParams + " " + sudParams.zeroPad); + + if (sudAudioPanel!=null) return sudAudioPanel.getParams(sudParams); + if (sudAudioPaneFX!=null) return sudAudioPaneFX.getParams(sudParams);; + + return null; + } + + + /** + * The sud settings panel. + */ + public class SudSettingsPanel extends PamPanel { + + private static final long serialVersionUID = 1L; + private JCheckBox zeroPadSud; + + public SudSettingsPanel() { + + this.setLayout(new GridBagLayout()); +// soundTrapDate.setPreferredSize(tzPanel.getPreferredSize()); + GridBagConstraints c = new PamGridBagContraints(); + c.gridy = 0; + c.gridx = 0; + c.gridwidth = 1; + + zeroPadSud = new JCheckBox("Zero pad sud files"); + zeroPadSud.setToolTipText(sudTooltip); + + this.add(zeroPadSud,c); + } + + public void setParams(PamSudParams sudParams) { + this.zeroPadSud.setSelected(sudParams.zeroPad); + } + + + public PamSudParams getParams(PamSudParams sudParams) { + sudParams.zeroPad = zeroPadSud.isSelected(); + return sudParams; + } + + } + + + /** + * The sud settings panel for JavaFX + */ + public class SudSettingsPane extends PamHBox { + + private PamToggleSwitch zeroPadSud; + + public SudSettingsPane() { + +// soundTrapDate.setPreferredSize(tzPanel.getPreferredSize()); + GridBagConstraints c = new PamGridBagContraints(); + c.gridy = 0; + c.gridx = 0; + c.gridwidth = 1; + + zeroPadSud = new PamToggleSwitch("Zero pad sud files"); + zeroPadSud.setTooltip(new Tooltip(sudTooltip)); + + this.getChildren().add(zeroPadSud); + } + + public void setParams(PamSudParams sudParams) { + this.zeroPadSud.setSelected(sudParams.zeroPad); + } + + + public PamSudParams getParams(PamSudParams sudParams) { + sudParams.zeroPad = zeroPadSud.isSelected(); + return sudParams; + } + + } + + + @Override + public void getParams() { + getParams(sudAudioFile.getSudParams()); + } + + @Override + public void setParams() { + setParams(sudAudioFile.getSudParams()); + } + +} diff --git a/src/PamUtils/PamArrayUtils.java b/src/PamUtils/PamArrayUtils.java index 37909be0..b6a4da62 100644 --- a/src/PamUtils/PamArrayUtils.java +++ b/src/PamUtils/PamArrayUtils.java @@ -564,6 +564,55 @@ public class PamArrayUtils { return index; } + /** + * Get the index of the maximum value in an array + * @param arr - the array to find the position of the maximum value. + * m value of. + * @return the index of the maximum value + */ + public static int maxPos(float[] arr) { + double max = Double.NEGATIVE_INFINITY; + int index = -1; + + int count = 0; + for(float cur: arr) { + if (cur>max) { + index = count; + max=cur; + } + count++; + } + + + return index; + } + + /** + * Get the index of the maximum value within a 2D matrix + * @param arr - the array to find the position of the maximum value. + * m value of. + * @return the index i and j of the maximum value + */ + public static int[] maxPos(float[][] arr) { + float max = Float.NEGATIVE_INFINITY; + int[] index = new int[] {-1,-1}; + + // int count = 0; + float cur; + for(int i=0; imax) { + index[0]=i; + index[1]=j; + max=cur; + } + // count++; + } + } + return index; + } + /** * Get the minimum index of an array * @param arr - the array to find the position of the maximum value. diff --git a/src/PamView/PamAWTUtils.java b/src/PamView/PamAWTUtils.java index 7280d460..fbefad34 100644 --- a/src/PamView/PamAWTUtils.java +++ b/src/PamView/PamAWTUtils.java @@ -159,4 +159,29 @@ public class PamAWTUtils { return bestPoint; } + + + /** + * Convert a colour to an int. + * @param c - the colour to change. + * @return the int representation of the colour + */ + public static int colorToInt(java.awt.Color c) { + int r = (int) Math.round(c.getRed()); + int g = (int) Math.round(c.getGreen()); + int b = (int) Math.round(c.getBlue()); + return (r << 16) | (g << 8) | b; + } + + /** + * Convert an int encoded with a colour to a Color object. + * @param value - the int to convert to colour + * @return the Color object for the int + */ + public static java.awt.Color intToColor(int value) { + int r = (value >>> 16) & 0xFF; + int g = (value >>> 8) & 0xFF; + int b = value & 0xFF; + return new java.awt.Color(r,g,b); + } } diff --git a/src/PamView/dialog/GenericSwingDialog.java b/src/PamView/dialog/GenericSwingDialog.java index 208bd386..72ea3821 100644 --- a/src/PamView/dialog/GenericSwingDialog.java +++ b/src/PamView/dialog/GenericSwingDialog.java @@ -10,6 +10,8 @@ import javax.swing.JComponent; import javax.swing.JPanel; import javax.swing.border.EmptyBorder; +import PamController.PamController; + public class GenericSwingDialog extends PamDialog { private boolean allOk; @@ -45,6 +47,9 @@ public class GenericSwingDialog extends PamDialog { * @return */ public static boolean showDialog(Window parentFrame, String title, Point screenPoint, PamDialogPanel ...dialogPanels) { + if (parentFrame == null) { + parentFrame = PamController.getMainFrame(); + } GenericSwingDialog swingDialog = new GenericSwingDialog(parentFrame, title, dialogPanels); swingDialog.setParams(); swingDialog.pack(); diff --git a/src/PamView/dialog/PamDialogPanel.java b/src/PamView/dialog/PamDialogPanel.java index d8dcc706..19e83808 100644 --- a/src/PamView/dialog/PamDialogPanel.java +++ b/src/PamView/dialog/PamDialogPanel.java @@ -5,6 +5,7 @@ import javax.swing.JComponent; /** * General class for dialog panels which will be incorporated into one or * more actual dialogs. + * Can be quickly opened with GenericSwingDialog * @author Doug Gillespie * */ diff --git a/src/PamView/symbol/modifier/PeakFreqModifier.java b/src/PamView/symbol/modifier/PeakFreqModifier.java index 1a8d504a..29a17765 100644 --- a/src/PamView/symbol/modifier/PeakFreqModifier.java +++ b/src/PamView/symbol/modifier/PeakFreqModifier.java @@ -5,6 +5,7 @@ import java.awt.Color; import PamUtils.PamUtils; import PamView.GeneralProjector; import PamView.PamSymbolType; +import PamView.dialog.PamDialogPanel; import PamView.symbol.PamSymbolChooser; import PamView.symbol.SymbolData; import PamguardMVC.PamDataUnit; @@ -45,7 +46,15 @@ public class PeakFreqModifier extends SymbolModifier { */ private ColourArrayType colourArrayType; - private PeakFreqOptionsPane peakFreqOptions; + /** + * JavaFX pane for frequency symbol options. + */ + private PeakFreqOptionsPane peakFreqOptionsPaneFX; + + /** + * Swing panel for frequency symbol options + */ + private PamDialogPanel peakFreqOptionsPanel; public PeakFreqModifier(PamSymbolChooser symbolChooser) { super(PEAK_FREQ_MODIFIER_NAME, symbolChooser, SymbolModType.FILLCOLOUR | SymbolModType.LINECOLOUR ); @@ -130,11 +139,20 @@ public class PeakFreqModifier extends SymbolModifier { @Override public SymbolModifierPane getOptionsPane() { //System.out.println("PEAK FREQ COLOUR ARRAY2: " + peakFreqSymbolOptions.freqColourArray); - if (this.peakFreqOptions==null) { - peakFreqOptions = new PeakFreqOptionsPane(this); - peakFreqOptions.setParams(); + if (this.peakFreqOptionsPaneFX==null) { + peakFreqOptionsPaneFX = new PeakFreqOptionsPane(this); + peakFreqOptionsPaneFX.setParams(); } - return peakFreqOptions; + return peakFreqOptionsPaneFX; + } + + public PamDialogPanel getDialogPanel() { + //System.out.println("PEAK FREQ COLOUR ARRAY2: " + peakFreqSymbolOptions.freqColourArray); + if (this.peakFreqOptionsPanel==null) { + peakFreqOptionsPanel = new PeakFreqOptionsPanel(this); + peakFreqOptionsPanel.setParams(); + } + return peakFreqOptionsPanel; } @Override diff --git a/src/PamView/symbol/modifier/PeakFreqOptionsPanel.java b/src/PamView/symbol/modifier/PeakFreqOptionsPanel.java new file mode 100644 index 00000000..f26d223e --- /dev/null +++ b/src/PamView/symbol/modifier/PeakFreqOptionsPanel.java @@ -0,0 +1,203 @@ +package PamView.symbol.modifier; + +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; + +import javax.swing.JComponent; +import javax.swing.JLabel; +import javax.swing.JSpinner; +import javax.swing.SpinnerNumberModel; +import javax.swing.border.TitledBorder; + +import PamView.ColourComboBox; +import PamView.dialog.PamDialogPanel; +import PamView.panel.PamPanel; +import pamViewFX.fxNodes.utilsFX.PamUtilsFX; + +/** + * Swing panel for changing parameters for the peak frequency symbol chooser. + * + * @author Jamie Macaulay + */ +public class PeakFreqOptionsPanel implements PamDialogPanel { + + private PeakFreqModifier freqSymbolModifer; + + private PamPanel mainPanel; + + private JSpinner minFreq; + + private JSpinner maxFreq; + + private ColourComboBox colourBox; + + public PeakFreqOptionsPanel(PeakFreqModifier symbolModifer) { + this.freqSymbolModifer=symbolModifer; + mainPanel = createFreqPaneL(); + } + + + /** + * Pane which changes the frequency limits. + * @return pane with controls to change freq. limits. + */ + private PamPanel createFreqPaneL(){ + + PamPanel holder = new PamPanel(); + holder.setLayout(new GridBagLayout()); + holder.setBorder(new TitledBorder("Peak frequency colour map")); + + GridBagConstraints c = new GridBagConstraints(); + c.gridy=0; + c.gridx=0; + + //doesn't seem to work so added spaces in labels instead + c.ipadx = 5; + + + c.anchor =GridBagConstraints.EAST; + holder.add(new JLabel("Min. freq"), c); + + c.gridx++; + c.anchor =GridBagConstraints.WEST; + minFreq = new JSpinner(new SpinnerNumberModel(0., 0., 10000000., 1000.)); + //make the lock button the same height as the spinner + Dimension prefSize = minFreq.getPreferredSize(); + minFreq.setPreferredSize(new Dimension(90, prefSize.height)); + + holder.add(minFreq, c); + + c.gridx++; + c.ipadx = 5; + c.anchor =GridBagConstraints.EAST; + holder.add(new JLabel(" Max. freq"), c); + + c.gridx++; + c.ipadx = 5; + c.anchor =GridBagConstraints.WEST; + maxFreq =new JSpinner(new SpinnerNumberModel(1000., 1., Math.max(1000.,freqSymbolModifer.getSymbolChooser().getPamDataBlock().getSampleRate()/2.), 1000.)); + maxFreq.setPreferredSize(new Dimension(90, prefSize.height)); + holder.add(maxFreq, c); + + c.gridx++; + c.ipadx = 5; + c.anchor =GridBagConstraints.WEST; + holder.add(new JLabel(" Hz"), c); + + + c.gridy++; + c.ipadx = 5; + c.gridwidth=2; + + c.gridx=0; + c.anchor =GridBagConstraints.EAST; + holder.add(new JLabel("Freq. colour map"), c); + + c.gridx=2; + c.gridwidth=3; + colourBox = new ColourComboBox(); + c.anchor =GridBagConstraints.WEST; + prefSize = colourBox.getPreferredSize(); + colourBox.setPreferredSize(new Dimension(200, prefSize.height)); + holder.add(colourBox, c); + + return holder; + + } + + @Override + public JComponent getDialogComponent() { + return mainPanel; + } + + + + + + @Override + public boolean getParams(){ + + //bit messy but works / +// PeakFreqSymbolOptions symbolOptions = (PeakFreqSymbolOptions) standardSymbolOptions.getModifierParams(this.getSymbolModifier().getName()); + + PeakFreqSymbolOptions symbolOptions = (PeakFreqSymbolOptions)freqSymbolModifer.getSymbolModifierParams().clone(); + //must make sure we do not call get parameters during a set parameters - the listeners on the controls call getParams so all goes + //haywire if the setParams is not set properly. + //System.out.println("GETPARAMS: " + ColourArray.getColorArrayType(this.colourBox.getSelectionModel().getSelectedItem()) + " " + setParams); + symbolOptions.freqLimts=new double[] {(double) minFreq.getValue(), (double) maxFreq.getValue()}; + symbolOptions.freqColourArray = PamUtilsFX.swingColArray2FX(this.colourBox.getSelectedColourMap()); + + //System.out.println("StandardSymbolModifierPane : getParams(): new mod: " +mod); + + freqSymbolModifer.setSymbolModifierParams(symbolOptions); + + System.out.println("Get freq limits: 1: " + symbolOptions.freqLimts[0] + " " + symbolOptions.freqLimts[1] + " " + (double) maxFreq.getValue()); + + + return true; + + } + + @Override + public void setParams() { + +// StandardSymbolOptions standardSymbolOptions = (StandardSymbolOptions) getSymbolModifier().getSymbolChooser().getSymbolOptions(); +// PeakFreqSymbolOptions symbolOptions = (PeakFreqSymbolOptions) standardSymbolOptions.getModifierParams(this.getSymbolModifier().getName()); + PeakFreqSymbolOptions symbolOptions = (PeakFreqSymbolOptions)freqSymbolModifer.getSymbolModifierParams(); + + //now set frequency parameters; + checkFreqLimits( symbolOptions ) ; + + minFreq.setValue(symbolOptions.freqLimts[0]); + maxFreq.setValue(symbolOptions.freqLimts[1]); + + colourBox.setSelectedColourMap((PamUtilsFX.fxColArray2Swing(symbolOptions.freqColourArray))); + } + + /** + * Check the frequency limits make sense for the given datablock + * @param symbolOptions - the peak frequency options. + */ + private void checkFreqLimits(PeakFreqSymbolOptions symbolOptions ) { + + System.out.println("Check freq limits: 1: " + symbolOptions.freqLimts[0] + " " + symbolOptions.freqLimts[1]); + + SpinnerNumberModel spinnerValFact = (SpinnerNumberModel) maxFreq.getModel(); + spinnerValFact.setMaximum(getSampleRate() /2.); + //for some reason also need to set this + spinnerValFact.setMinimum(1.); + + //set reasonable step sizes + if (getSampleRate()>=10000) { + spinnerValFact.setStepSize(1000.); + } + else if (getSampleRate()>=2000){ + spinnerValFact.setStepSize(200.); + } + else { + spinnerValFact.setStepSize(50.); + } + + if (symbolOptions.freqLimts==null) { + symbolOptions.freqLimts= new double[] {0, getSampleRate() /2}; + } + System.out.println("Check freq limits: 2: " + symbolOptions.freqLimts[0] + " " + symbolOptions.freqLimts[1]); + //check nyquist for upper limit + if (symbolOptions.freqLimts[1]>getSampleRate() /2) { + symbolOptions.freqLimts[1]=getSampleRate() /2; + } + //check nyquist for lower limit + if (symbolOptions.freqLimts[0]>getSampleRate() /2) { + symbolOptions.freqLimts[0]=0; + } + } + + + + private float getSampleRate() { + return freqSymbolModifer.getSymbolChooser().getPamDataBlock().getSampleRate(); + } + + +} diff --git a/src/PamView/symbol/modifier/SymbolModifier.java b/src/PamView/symbol/modifier/SymbolModifier.java index 3e33d0eb..96ccfa53 100644 --- a/src/PamView/symbol/modifier/SymbolModifier.java +++ b/src/PamView/symbol/modifier/SymbolModifier.java @@ -150,7 +150,8 @@ abstract public class SymbolModifier { if (dialogPanel == null) { return null; } - JMenuItem menuItem = new JMenuItem("Options ..."); + JMenuItem menuItem = new JMenuItem("More options ..."); + menuItem.setToolTipText("More symbol options"); menuItem.addActionListener(new ActionListener() { @Override diff --git a/src/PamView/symbol/modifier/swing/ModifierTreeNode.java b/src/PamView/symbol/modifier/swing/ModifierTreeNode.java index 746fa43b..70285206 100644 --- a/src/PamView/symbol/modifier/swing/ModifierTreeNode.java +++ b/src/PamView/symbol/modifier/swing/ModifierTreeNode.java @@ -8,12 +8,13 @@ import java.util.Enumeration; import javax.swing.tree.TreeNode; import PamUtils.PamUtils; +import PamView.dialog.PamDialogPanel; import PamView.symbol.modifier.SymbolModifier; public class ModifierTreeNode implements TreeNode { private SymbolModifier modifier; private SymbolTreeRoot rootNode; - private ArrayList choiceNodes; + private ArrayList choiceNodes; public ModifierTreeNode(SymbolTreeRoot rootNode, SymbolModifier modifier) { super(); @@ -22,17 +23,25 @@ public class ModifierTreeNode implements TreeNode { int modBits = modifier.getModifyableBits(); int nMod = Integer.bitCount(modBits); choiceNodes = new ArrayList<>(); + + int leafIndex = 0; + for (int i = 0; i < nMod; i++) { - choiceNodes.add(new ChoiceTreeNode(this, 1< children() { + return null; + } + +} diff --git a/src/PamView/symbol/modifier/swing/SymbolModifierPanel.java b/src/PamView/symbol/modifier/swing/SymbolModifierPanel.java index 922a4c16..ddef072e 100644 --- a/src/PamView/symbol/modifier/swing/SymbolModifierPanel.java +++ b/src/PamView/symbol/modifier/swing/SymbolModifierPanel.java @@ -18,12 +18,10 @@ import javax.swing.tree.TreePath; import PamView.dialog.DialogScrollPane; import PamView.dialog.PamDialogPanel; -import PamView.symbol.PamSymbolChooser; import PamView.symbol.StandardSymbolChooser; import PamView.symbol.StandardSymbolOptions; import PamView.symbol.modifier.SymbolModifier; import PamView.symbol.modifier.SymbolModifierParams; -import PamguardMVC.debug.Debug; /** * Panel to include in a dialog with options to select and activate a variety of symbol modifiers. @@ -137,6 +135,7 @@ public class SymbolModifierPanel implements PamDialogPanel { JMenuItem optsItem = modifier.getModifierOptionsMenu(); if (optsItem != null) { popMenu.add(optsItem); + popMenu.addSeparator(); } } if (nodeInd > 0) { diff --git a/src/PamView/symbol/modifier/swing/SymbolTreeRenderer.java b/src/PamView/symbol/modifier/swing/SymbolTreeRenderer.java index da04fedf..f96d563b 100644 --- a/src/PamView/symbol/modifier/swing/SymbolTreeRenderer.java +++ b/src/PamView/symbol/modifier/swing/SymbolTreeRenderer.java @@ -43,6 +43,10 @@ public class SymbolTreeRenderer extends DefaultTreeCellRenderer { if (value instanceof ChoiceTreeNode) { return checkboxChoice(tree, (ChoiceTreeNode) value, sel, expanded, leaf, row, hasFocus); } + + if (value instanceof OptionsTreeNode) { + return ((OptionsTreeNode) value).optionsButton; + } Component component = super.getTreeCellRendererComponent(tree, value, sel, expanded, leaf, row, hasFocus); if (value instanceof ModifierTreeNode) { diff --git a/src/PamguardMVC/RawDataTransforms.java b/src/PamguardMVC/RawDataTransforms.java index 44690ed1..2c7380b3 100644 --- a/src/PamguardMVC/RawDataTransforms.java +++ b/src/PamguardMVC/RawDataTransforms.java @@ -411,7 +411,15 @@ public class RawDataTransforms { paddedRawData = new double[fftLength]; rawData = getWaveData(channel); //double[] rotData = getRotationCorrection(channel); - mn = Math.min(fftLength, getSampleDuration().intValue()); + + /** + *FIXME + * 11/07 Changed from getSampleDuration because an error sometimes occurs where the sample duration + * is not the same as the wavefom length...not sure why. + */ + //mn = Math.min(fftLength, getSampleDuration().intValue()); + mn = Math.min(fftLength, rawData.length); +// System.out.println("fftLength: " + rawData.length + " " + getSampleDuration().intValue() + " mn " +mn); for (i = 0; i < mn; i++) { paddedRawData[i] = rawData[i];//-rotData[i]; } diff --git a/src/PamguardMVC/dataSelector/CompoundDataSelector.java b/src/PamguardMVC/dataSelector/CompoundDataSelector.java index 923baf6c..2060e419 100644 --- a/src/PamguardMVC/dataSelector/CompoundDataSelector.java +++ b/src/PamguardMVC/dataSelector/CompoundDataSelector.java @@ -13,6 +13,8 @@ public class CompoundDataSelector extends DataSelector { private CompoundParams compoundParams = new CompoundParams(); + private CompoundDialogPaneFX compoundPaneFX; + public CompoundDataSelector(PamDataBlock pamDataBlock, ArrayList allSelectors, String selectorName, boolean allowScores, String selectorType) { super(pamDataBlock, selectorName, allowScores); @@ -57,8 +59,10 @@ public class CompoundDataSelector extends DataSelector { @Override public DynamicSettingsPane getDialogPaneFX() { - // TODO Auto-generated method stub - return null; + if (compoundPaneFX==null) { + compoundPaneFX = new CompoundDialogPaneFX(this); + } + return compoundPaneFX; } @Override @@ -91,6 +95,9 @@ public class CompoundDataSelector extends DataSelector { score = Math.max(score, score2); // take the largest } } + +// System.out.println("Hello Compound Data selector: " + score); + return score; } @@ -101,4 +108,25 @@ public class CompoundDataSelector extends DataSelector { return selectorList; } + @Override + public DataSelector findDataSelector(Class selectorClass) { + /** + * Check this, then all in the selector list. Should iterate happily back to + * a basic data selector as required even if there are multiple Compound ones. + */ + if (this.getClass() == selectorClass) { + return this; + } + if (selectorList == null) { + return null; + } + for (DataSelector aSelector : selectorList) { + DataSelector subSel = aSelector.findDataSelector(selectorClass); + if (subSel != null) { + return subSel; + } + } + return null; + } + } diff --git a/src/PamguardMVC/dataSelector/CompoundDialogPaneFX.java b/src/PamguardMVC/dataSelector/CompoundDialogPaneFX.java new file mode 100644 index 00000000..bb5ce81e --- /dev/null +++ b/src/PamguardMVC/dataSelector/CompoundDialogPaneFX.java @@ -0,0 +1,89 @@ +package PamguardMVC.dataSelector; + +import java.util.ArrayList; + +import javafx.scene.Node; +import pamViewFX.fxNodes.PamVBox; +import pamViewFX.fxSettingsPanes.DynamicSettingsPane; + + +/** + * shows multiple data selectors in a pane. This would be used, for example, in a situation where + * a data unit has multiple associated data selectors, for example if is annotated with an annotation + * that has a data selector. + */ +public class CompoundDialogPaneFX extends DynamicSettingsPane { + + + private CompoundDataSelector compoundDataSelector; + + private ArrayList selectorList; + private ArrayList selectorPanels; + + private PamVBox mainPanel; + + public CompoundDialogPaneFX(CompoundDataSelector compoundDataSelector) { + super(null); + + this.compoundDataSelector = compoundDataSelector; + this.selectorList = compoundDataSelector.getSelectorList(); + + mainPanel = new PamVBox(); + mainPanel.setSpacing(10); + selectorPanels = new ArrayList(selectorList.size()); + int ind = 0; + for (DataSelector ds : selectorList) { + DynamicSettingsPane panel = ds.getDialogPaneFX(); + // turn all these panels into the compound ones with the extra enable options. + + DataSelectorDialogPaneFX dsp = new DataSelectorDialogPaneFX(ds, panel, ind++); + selectorPanels.add(dsp); + + //need to add a listener for settings changes to these panes so it can be passed to this pane + dsp.addSettingsListener(()->{ + //notify any listeners to this pane that the sub pane has changed. + notifySettingsListeners(); + }); + + mainPanel.getChildren().add(dsp.getContentNode()); + } + + + } + + + @Override + public Boolean getParams(Boolean currParams) { + boolean ok = true; + for (int i = 0; i < selectorPanels.size(); i++) { + DataSelectorDialogPaneFX panel = selectorPanels.get(i); + ok |= panel.getParams(currParams); + } + return ok; + } + + @Override + public void setParams(Boolean input) { + for (int i = 0; i < selectorPanels.size(); i++) { + DataSelectorDialogPaneFX panel = selectorPanels.get(i); + panel.setParams(input); + } + } + + @Override + public String getName() { + return "Compound data selector pane"; + } + + @Override + public Node getContentNode() { + return mainPanel; + } + + @Override + public void paneInitialized() { + // TODO Auto-generated method stub + + } + +} diff --git a/src/PamguardMVC/dataSelector/CompoundDialogPanel.java b/src/PamguardMVC/dataSelector/CompoundDialogPanel.java index 6ce8cc0f..8f648caa 100644 --- a/src/PamguardMVC/dataSelector/CompoundDialogPanel.java +++ b/src/PamguardMVC/dataSelector/CompoundDialogPanel.java @@ -1,11 +1,9 @@ package PamguardMVC.dataSelector; -import java.awt.Color; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.util.ArrayList; -import javax.swing.BoxLayout; import javax.swing.JComponent; import javax.swing.JPanel; diff --git a/src/PamguardMVC/dataSelector/DataSelectDialog.java b/src/PamguardMVC/dataSelector/DataSelectDialog.java index 5fd7f81f..9cb541e1 100644 --- a/src/PamguardMVC/dataSelector/DataSelectDialog.java +++ b/src/PamguardMVC/dataSelector/DataSelectDialog.java @@ -76,6 +76,22 @@ public class DataSelectDialog extends PamDialog { // } } + public boolean setTab(int tabIndex) { + if (tabPane == null) { + return false; + } + if (tabIndex < 0 || tabIndex >= tabPane.getTabCount()) { + return false; + } + try { + tabPane.setSelectedIndex(tabIndex); + } + catch (Exception e) { + return false; + } + return true; + } + public boolean showDialog() { if (dataPanel != null) { dataPanel.setParams(); diff --git a/src/PamguardMVC/dataSelector/DataSelector.java b/src/PamguardMVC/dataSelector/DataSelector.java index b033976c..e1573752 100644 --- a/src/PamguardMVC/dataSelector/DataSelector.java +++ b/src/PamguardMVC/dataSelector/DataSelector.java @@ -97,6 +97,23 @@ public abstract class DataSelector { */ abstract public DynamicSettingsPane getDialogPaneFX(); + /** + * Find a data selector within a data selector. This is primarily for use with + * ComoundDataSelector objects which may encapsulate multiple other selectors, + * particularly when annotations are in use. but it's needed here so that it + * can be called on any DataSelector object. + * @param selectorClass class to data selector to find. + * @return DataSelector or null if that class of data selector doesn't exist. + */ + public DataSelector findDataSelector(Class selectorClass) { + if (this.getClass() == selectorClass) { + return this; + } + else { + return null; + } + } + /** * Get a menu item for the data selector that can be easily added * to any other menu. diff --git a/src/PamguardMVC/dataSelector/DataSelectorDialogPaneFX.java b/src/PamguardMVC/dataSelector/DataSelectorDialogPaneFX.java new file mode 100644 index 00000000..3782ed88 --- /dev/null +++ b/src/PamguardMVC/dataSelector/DataSelectorDialogPaneFX.java @@ -0,0 +1,213 @@ +package PamguardMVC.dataSelector; + +import org.controlsfx.control.SegmentedButton; + +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.Label; +import javafx.scene.control.Separator; +import javafx.scene.control.ToggleButton; +import javafx.scene.control.ToggleGroup; +import javafx.scene.control.Tooltip; +import javafx.scene.layout.HBox; +import pamViewFX.PamGuiManagerFX; +import pamViewFX.fxNodes.PamVBox; +import pamViewFX.fxSettingsPanes.DynamicSettingsPane; + + +/** + * Dialog panel to wrap around a standard dialog panel from a data selector. + * This adds a wrapper the data selector which enables or disables it based on + * whether it has been selected or not. + * @author Jamie Macaulay + * + */ +public class DataSelectorDialogPaneFX extends DynamicSettingsPane { + + private static final double PREF_TOGGLE_WIDTH = 60; + + private DataSelector dataSelector; + private DynamicSettingsPane innerPanel; + private int setIndex; + + private PamVBox dsPane; + private ToggleGroup buttonGroup; + private ToggleButton andButton, orButton, disableButton; + private HBox buttonPane; + private Node contentPane; + + /** + * Create the DataSelectorDialogPaneFX + * @param dataSelector + * @param innerPanel + * @param setIndex + */ + public DataSelectorDialogPaneFX(DataSelector dataSelector, DynamicSettingsPane innerPanel, int setIndex) { + super(null); + this.dataSelector = dataSelector; + this.innerPanel = innerPanel; + this.setIndex = setIndex; + + dsPane = new PamVBox(); // Use VBox for vertical layout + dsPane.setSpacing(5); + + contentPane = innerPanel.getContentNode(); + + // Border exBorder = innerComponent.getBorder(); + // + // if (exBorder instanceof TitledBorder) { + // innerComponent.setBorder(null); + // // Set a lower bevel border if desired: + // // innerComponent.setBorder(new BevelBorder(BevelBorder.LOWERED)); + // dsPane.setBorder(exBorder); + // } else { + // dsPane.setBorder(new TitledBorder(dataSelector.getSelectorTitle())); + // } + + Label title = new Label(innerPanel.getName()); + title.setTooltip(new Tooltip("Data selector: " + dataSelector.getLongSelectorName())); + PamGuiManagerFX.titleFont2style(title); + + + buttonGroup = new ToggleGroup(); + andButton = new ToggleButton(setIndex == 0 ? "Enable" : "AND"); + andButton.setPrefWidth(PREF_TOGGLE_WIDTH); + + disableButton = new ToggleButton("Skip"); + disableButton.setPrefWidth(PREF_TOGGLE_WIDTH); + + orButton = new ToggleButton("OR"); + orButton.setPrefWidth(PREF_TOGGLE_WIDTH); + + buttonGroup.getToggles().addAll(andButton, orButton, disableButton); + + SegmentedButton segmentedButton ; + if (setIndex > 0) { + segmentedButton = new SegmentedButton(andButton, orButton, disableButton); + } + else { + segmentedButton = new SegmentedButton(andButton, disableButton); + + } + + + andButton.setOnAction(event -> { + enableComponent(); + notifySettingsListeners(); + }); + orButton.setOnAction(event ->{ + enableComponent(); + notifySettingsListeners(); + }); + disableButton.setOnAction(event ->{ + enableComponent(); + notifySettingsListeners(); + }); + + buttonPane = new HBox(); // Use HBox for horizontal button layout + buttonPane.setSpacing(5); + buttonPane.setAlignment(Pos.CENTER); + buttonPane.getChildren().addAll(segmentedButton); + + //add everything to the main pane + if (setIndex > 0) { + dsPane.getChildren().add(new Separator()); + } + dsPane.getChildren().add(title); + dsPane.getChildren().add(buttonPane); // Add button pane at top + dsPane.getChildren().add(contentPane); + + + //need to add a listener for settings changes to these panes so it can be passed to this pane + innerPanel.addSettingsListener(()->{ + notifySettingsListeners(); + }); + + + orButton.setVisible(setIndex > 0); + if (dataSelector instanceof CompoundDataSelector || setIndex < 0) { + buttonPane.setVisible(false); + dsPane.setBorder(null); + } + enableComponent(); + //buttonPane.setTooltip(new Tooltip("Options for " + dataSelector.getLongSelectorName())); + } + + + private void enableComponent() { + contentPane.setDisable(disableButton.isSelected()); + } + + @Override + public void setParams(Boolean input) { + DataSelectParams params = dataSelector.getParams(); + if (params == null) { + return; + } + if (buttonPane.isVisible()) { + andButton.setSelected(params.getCombinationFlag() == DataSelectParams.DATA_SELECT_AND); + orButton.setSelected(params.getCombinationFlag() == DataSelectParams.DATA_SELECT_OR); + disableButton.setSelected(params.getCombinationFlag() == DataSelectParams.DATA_SELECT_DISABLE); + } else { + andButton.setSelected(true); + orButton.setSelected(false); + disableButton.setSelected(false); + } + + innerPanel.setParams(input); + enableComponent(); + } + + @Override + public Boolean getParams(Boolean a) { + DataSelectParams params = dataSelector.getParams(); + + if (disableButton.isSelected()) { + if (params != null) { + params.setCombinationFlag(DataSelectParams.DATA_SELECT_DISABLE); + } + return true; + } + + boolean innerOk = innerPanel.getParams(a); + if (!innerOk) { + return false; + } + + params = dataSelector.getParams(); // Might have been recreated + + if (params != null) { + if (andButton.isSelected()) { + params.setCombinationFlag(DataSelectParams.DATA_SELECT_AND); + } else if (orButton.isSelected()) { + params.setCombinationFlag(DataSelectParams.DATA_SELECT_OR); + } else if (disableButton.isSelected()) { + params.setCombinationFlag(DataSelectParams.DATA_SELECT_DISABLE); + } + } + + return innerOk; + } + + // Utility method for disabling/enabling a Node (doesn't work recursively) + public static void setEnabled(Node node, boolean enabled) { + + } + + + @Override + public String getName() { + return "Data selector"; + } + + @Override + public Node getContentNode() { + return dsPane; + } + + @Override + public void paneInitialized() { + // TODO Auto-generated method stub + + } +} diff --git a/src/Resources/css/pamSettingsCSS.css b/src/Resources/css/pamSettingsCSS.css index af9f9cf9..9bec582c 100644 --- a/src/Resources/css/pamSettingsCSS.css +++ b/src/Resources/css/pamSettingsCSS.css @@ -987,3 +987,27 @@ .spinner { -fx-pref-width: 100px; } + + +/******************************************** +* * +* Segmented button * +* * +*********************************************/ + +.segmented-button .toggle-button.left-pill { + -fx-border-radius: 5 0 0 5; + -fx-background-radius: 5 0 0 5; +} + +.segmented-button .toggle-button.center-pill { + -fx-border-radius: 0 0 0 0; + -fx-background-radius: 0 0 0 0; +} + +.segmented-button .toggle-button.right-pill { + -fx-border-radius: 0 5 5 0; + -fx-background-radius: 0 5 5 0; +} + + diff --git a/src/Spectrogram/ColourRangeSlider.java b/src/Spectrogram/ColourRangeSlider.java index c5e851c1..61b48400 100644 --- a/src/Spectrogram/ColourRangeSlider.java +++ b/src/Spectrogram/ColourRangeSlider.java @@ -6,6 +6,11 @@ import PamView.ColourArray; import PamView.ColourArray.ColourArrayType; import PamView.sliders.PamRangeSlider; +/** + * A range slider which shows a colour gradient between two thumbs. + * + * @author Jamie Macaulay + */ public class ColourRangeSlider extends PamRangeSlider { /** @@ -18,13 +23,29 @@ public class ColourRangeSlider extends PamRangeSlider { } public ColourRangeSlider(int min, int max){ - super(min,max, JSlider.VERTICAL); + super(min, max,JSlider.VERTICAL); } + public ColourRangeSlider(int min, int max, int orientation){ + super(min, max,orientation); + } + + public ColourRangeSlider(int orientation){ + super(orientation); + } + + /** + * Set the colour map type to show between the two thumbs. + * @param colourMap - the colour map to show. + */ public void setColourMap(ColourArrayType colourMap){ ((ColourRangeSliderUI) getUI()).setColourMap(colourMap); } + /** + * Set a custom colour map type to show between the two thumbs. + * @param colourMap - the colour map to show. + */ public void setColourMap(ColourArray colourMap){ ((ColourRangeSliderUI) getUI()).setColourMap(colourMap); } diff --git a/src/Spectrogram/ColourRangeSliderUI.java b/src/Spectrogram/ColourRangeSliderUI.java index 808aa6a4..884db474 100644 --- a/src/Spectrogram/ColourRangeSliderUI.java +++ b/src/Spectrogram/ColourRangeSliderUI.java @@ -8,8 +8,7 @@ import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.awt.image.WritableRaster; - - +import javax.swing.JSlider; import PamView.ColourArray; import PamView.ColourArray.ColourArrayType; @@ -78,6 +77,8 @@ public class ColourRangeSliderUI extends PamRangeSliderUI { private void createColourMapImage(){ + if (b.getOrientation() == JSlider.VERTICAL) { + // now make a standard amplitude image if (colourArray != null && colourArray.length > 0) { amplitudeImage = new BufferedImage(1, colourArray.length, @@ -87,6 +88,15 @@ public class ColourRangeSliderUI extends PamRangeSliderUI { raster.setPixel(0, colourArray.length - i - 1, colourArray[i]); } } + } + else { + amplitudeImage = new BufferedImage(colourArray.length, 1, + BufferedImage.TYPE_INT_RGB); + WritableRaster raster = amplitudeImage.getRaster(); + for (int i = 0; i < colourArray.length; i++) { + raster.setPixel(i, 0,colourArray[i]); + } + } } @@ -119,26 +129,98 @@ public class ColourRangeSliderUI extends PamRangeSliderUI { Rectangle trackBounds = trackRect; - // Determine position of selected range by moving from the middle - // of one thumb to the other. - int lowerY = thumbRect.y + (thumbRect.width / 2); - int upperY = getUpperThumbRect().y + (getUpperThumbRect().width / 2); - - // Determine track position. - int cx = (trackBounds.width / 2) - 2; - // Save colour and shift position. Color oldColor = g.getColor(); - g.translate(trackBounds.x + cx, trackBounds.y); - drawColourMapVert( g, lowerY - trackBounds.y, upperY - trackBounds.y, -(getUpperThumbRect().width /4)-trackBounds.x,(getUpperThumbRect().width / 2)-trackBounds.x+(trackBounds.width / 4)+2); + int cx; + if (b.getOrientation() == JSlider.VERTICAL) { + + // Determine position of selected range by moving from the middle + // of one thumb to the other. + int lowerY = thumbRect.y + (thumbRect.width / 2); + int upperY = getUpperThumbRect().y + (getUpperThumbRect().width / 2); + + // Determine track position. + cx = (trackBounds.width / 2) - 2; + + g.translate(trackBounds.x + cx, trackBounds.y); + drawColourMapVert( g, lowerY - trackBounds.y, upperY - trackBounds.y, -(getUpperThumbRect().width /4)-trackBounds.x,(getUpperThumbRect().width / 2)-trackBounds.x+(trackBounds.width / 4)+2); + + cx = (trackBounds.width / 2) - 2; + g.translate(-(trackBounds.x + cx), -trackBounds.y); + + } + else { + // Determine position of selected range by moving from the middle + // of one thumb to the other. + int lowerX = thumbRect.x; + int upperX = getUpperThumbRect().x; + + // Determine track position. + cx = (trackBounds.width / 2) - 2; +// -(getUpperThumbRect().height /4)-trackBounds.y, (getUpperThumbRect().height / 2)-trackBounds.y+(trackBounds.height / 4)+2 + drawColourMapHorz(g, trackBounds.height/2-getUpperThumbRect().height/2, trackBounds.height/2+getUpperThumbRect().height/2, lowerX + getUpperThumbRect().width/2, upperX+ getUpperThumbRect().width/2); + + } // Restore position and colour. - g.translate(-(trackBounds.x + cx), -trackBounds.y); g.setColor(oldColor); } + /** + * Draw the colour map between the two thumbs in the slider bar. Colour the section above the top most thumb + * and the section below the lower most thumb with the color map extremes. + * @param g- graphics + * @param y1 + * @param y2 + * @param x1 + * @param x2 + */ + private void drawColourMapHorz(Graphics g, int y1, int y2, int x1, int x2){ + + if (amplitudeImage == null) return; + + Graphics2D g2d = (Graphics2D) g; + + int width=Math.abs(x2-x1); + int height=Math.abs(y2-y1); + +// System.out.println("Width: " + width + " " + height + " x1 " + x1); + + //calculate the distance between thumbs + double ascaleX = width + / (double) amplitudeImage.getWidth(null); + double ascaleY = height + / (double) amplitudeImage.getHeight(null); + + AffineTransform xform = new AffineTransform(); + // xform.translate(1, amplitudeImage.getWidth(null)); + xform.scale(ascaleX, ascaleY); + //translate to the correct location; + g2d.translate(x1, y1); + //now translate back for the rest of the operations; + g2d.drawImage(amplitudeImage, xform, b); + + //translate back to our original position. + g2d.translate(-x1, -y1); + + //go to the left of the lower thumb; +// g2d.translate(0, height); + g2d.setColor(new Color((int) colourArray[0][0],(int)colourArray[0][1],(int) colourArray[0][2])); + for (int i=y1; i 10) { @@ -213,14 +217,14 @@ public class BTAmplitudeSelector implements PamDialogPanel { } private void setParams(BTDisplayParameters btParams) { amplitudeSelect.setSelected(btParams.amplitudeSelect); - minAmplitude.setText(String.format("%3.1f", btParams.minAmplitude)); + minAmplitude.setText(String.format("%3.1f", dataSelector.getParams().minimumAmplitude)); enableControls(); } private boolean getParams(BTDisplayParameters btParams) { btParams.amplitudeSelect = amplitudeSelect.isSelected(); try { - btParams.minAmplitude = Double.valueOf(minAmplitude.getText()); + dataSelector.getParams().minimumAmplitude = Double.valueOf(minAmplitude.getText()); } catch (NumberFormatException e) { return false; diff --git a/src/clickDetector/BTDisplayParameters.java b/src/clickDetector/BTDisplayParameters.java index bf9604f7..925f0b74 100644 --- a/src/clickDetector/BTDisplayParameters.java +++ b/src/clickDetector/BTDisplayParameters.java @@ -44,17 +44,17 @@ public class BTDisplayParameters implements Serializable, Cloneable, ManagedPara public int nBearingGridLines = 1; public int nAmplitudeGridLines = 0; public int nICIGridLines = 0; - public boolean showEchoes = true; +// public boolean showEchoes = true; public int minClickLength = 2, maxClickLength = 12; public int minClickHeight = 2, maxClickHeight = 12; private double timeRange = 10; public int displayChannels = 0; public boolean view360; public boolean amplitudeSelect = false; - public double minAmplitude = 0; +// public double minAmplitude = 0; public boolean showUnassignedICI = false; - public boolean showEventsOnly = false; - public boolean showANDEvents = true; +// public boolean showEventsOnly = false; +// public boolean showANDEvents = true; public boolean logICIScale; public int angleRotation = ROTATE_TOARRAY; @@ -65,7 +65,7 @@ public class BTDisplayParameters implements Serializable, Cloneable, ManagedPara /* * Show identified species */ - private boolean[] showSpeciesList; +// private boolean[] showSpeciesList; public int colourScheme = COLOUR_BY_TRAIN; @@ -83,7 +83,7 @@ public class BTDisplayParameters implements Serializable, Cloneable, ManagedPara } catch (CloneNotSupportedException Ex) { Ex.printStackTrace(); } - showSpeciesList = null; +// showSpeciesList = null; return null; } @@ -107,49 +107,49 @@ public class BTDisplayParameters implements Serializable, Cloneable, ManagedPara /** * @return the showSpeciesList */ - public boolean getShowSpecies(int speciesIndex) { - if (showSpeciesList != null && showSpeciesList.length > speciesIndex) { - return showSpeciesList[speciesIndex]; - } - makeShowSpeciesList(speciesIndex); - return true; - } - private void makeShowSpeciesList(int maxIndex) { - if (showSpeciesList == null) { - showSpeciesList = new boolean[0]; - } - else if (showSpeciesList.length > maxIndex) { - return; - } - int oldLength = showSpeciesList.length; - showSpeciesList = Arrays.copyOf(showSpeciesList, maxIndex + 1); - for (int i = oldLength; i <= maxIndex; i++) { - showSpeciesList[i] = true; - } - } +// public boolean getShowSpecies(int speciesIndex) { +// if (showSpeciesList != null && showSpeciesList.length > speciesIndex) { +// return showSpeciesList[speciesIndex]; +// } +// makeShowSpeciesList(speciesIndex); +// return true; +// } +// private void makeShowSpeciesList(int maxIndex) { +// if (showSpeciesList == null) { +// showSpeciesList = new boolean[0]; +// } +// else if (showSpeciesList.length > maxIndex) { +// return; +// } +// int oldLength = showSpeciesList.length; +// showSpeciesList = Arrays.copyOf(showSpeciesList, maxIndex + 1); +// for (int i = oldLength; i <= maxIndex; i++) { +// showSpeciesList[i] = true; +// } +// } /** * @param showSpeciesList the showSpeciesList to set */ - public void setShowSpecies(int speciesIndex, boolean showSpecies) { - makeShowSpeciesList(speciesIndex); - showSpeciesList[speciesIndex] = showSpecies; - } +// public void setShowSpecies(int speciesIndex, boolean showSpecies) { +// makeShowSpeciesList(speciesIndex); +// showSpeciesList[speciesIndex] = showSpecies; +// } @Override public PamParameterSet getParameterSet() { PamParameterSet ps = PamParameterSet.autoGenerate(this, ParameterSetType.DETECTOR); - try { - Field field = this.getClass().getDeclaredField("showSpeciesList"); - ps.put(new PrivatePamParameterData(this, field) { - @Override - public Object getData() throws IllegalArgumentException, IllegalAccessException { - return showSpeciesList; - } - }); - } catch (NoSuchFieldException | SecurityException e) { - e.printStackTrace(); - } +// try { +// Field field = this.getClass().getDeclaredField("showSpeciesList"); +// ps.put(new PrivatePamParameterData(this, field) { +// @Override +// public Object getData() throws IllegalArgumentException, IllegalAccessException { +// return showSpeciesList; +// } +// }); +// } catch (NoSuchFieldException | SecurityException e) { +// e.printStackTrace(); +// } return ps; } diff --git a/src/clickDetector/ClickBTDisplay.java b/src/clickDetector/ClickBTDisplay.java index 637d9578..eaa05353 100644 --- a/src/clickDetector/ClickBTDisplay.java +++ b/src/clickDetector/ClickBTDisplay.java @@ -34,6 +34,7 @@ import java.awt.Insets; import java.awt.Point; import java.awt.Rectangle; import java.awt.RenderingHints; +import java.awt.Window; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.AdjustmentEvent; @@ -115,9 +116,13 @@ import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataUnit; import PamguardMVC.PamObservable; import PamguardMVC.PamObserver; +import PamguardMVC.dataSelector.DataSelectDialog; +import PamguardMVC.dataSelector.DataSelector; import PamguardMVC.superdet.SuperDetection; import clickDetector.ClickClassifiers.ClickIdInformation; import clickDetector.ClickClassifiers.ClickIdentifier; +import clickDetector.alarm.ClickAlarmParameters; +import clickDetector.dataSelector.ClickDataSelector; import clickDetector.dialogs.ClickDisplayDialog; import clickDetector.offlineFuncs.OfflineEventDataBlock; import clickDetector.offlineFuncs.OfflineEventDataUnit; @@ -2240,11 +2245,15 @@ public class ClickBTDisplay extends ClickDisplay implements PamObserver, PamSett if (btDisplayParameters.amplitudeSelect == false) { return; } + ClickDataSelector clickDataSelector = getClickDataSelector(); + if (clickDataSelector == null) { + return; + } int n = countAmplitudeDeselected(); PamDataBlock clickData = clickControl.getClickDataBlock(); int nAll = clickData.getUnitsCount(); String txt = String.format("%d of %d loaded clicks will not be displayed because their amplitude is < %3.1fdB", - n, nAll, btDisplayParameters.minAmplitude); + n, nAll, clickDataSelector.getParams().minimumAmplitude); Insets insets = getInsets(); int x = insets.left; int y = getHeight()-5; @@ -2383,19 +2392,33 @@ public class ClickBTDisplay extends ClickDisplay implements PamObserver, PamSett } menuItem = new JMenuItem("Settings ..."); + menuItem.setToolTipText("Display options"); menuItem.addActionListener(new SettingsMenuAction()); menu.add(menuItem); + menuItem = new JMenuItem("Click data selector ..."); + menuItem.setToolTipText("Detailed data selection options"); + menuItem.addActionListener(new DataSelectorAction()); + menu.add(menuItem); menuItem = new JMenuItem("Show amplitude selector ..."); + menuItem.setToolTipText("Graphical amplitude selector display"); menuItem.addActionListener(new AmplitudeSelector()); menu.add(menuItem); menu.addSeparator(); - ArrayList colOptList = getSymbolChooser().getQuickMenuItems(clickControl.getGuiFrame(), this, "Colour by ", SymbolModType.EVERYTHING, true); + ArrayList colOptList = getSymbolChooser().getQuickMenuItems(clickControl.getGuiFrame(), this, "Colour by ", SymbolModType.EVERYTHING, false); if (colOptList != null) { for (JMenuItem menuIt : colOptList) { menu.add(menuIt); } - menu.addSeparator(); } + menuItem = new JMenuItem("More symbol options ..."); + menuItem.addActionListener(new ActionListener() { + @Override + public void actionPerformed(ActionEvent e) { + showDataSymbolOptions(1); + } + }); + menu.add(menuItem); + menu.addSeparator(); // menuItem = new JCheckBoxMenuItem("Colour by species id", // btDisplayParameters.colourScheme == BTDisplayParameters.COLOUR_BY_SPECIES); @@ -2470,7 +2493,7 @@ public class ClickBTDisplay extends ClickDisplay implements PamObserver, PamSett return menu; } - + /** * Looks through the current modules and finds if there is a target motion or static localiser */ @@ -2771,7 +2794,7 @@ public class ClickBTDisplay extends ClickDisplay implements PamObserver, PamSett BTDisplayParameters newParameters = ClickDisplayDialog.showDialog(clickControl, - clickControl.getGuiFrame(), btDisplayParameters); + clickControl.getGuiFrame(), btDisplayParameters, getClickDataSelector().getClickAlarmParameters()); if (newParameters != null){ btDisplayParameters = newParameters.clone(); if (getVScaleManager() != null) { @@ -2788,6 +2811,30 @@ public class ClickBTDisplay extends ClickDisplay implements PamObserver, PamSett } } + + + /** + * Get a data selector specific to this display. + * @return click data selector specific to this display. + */ + public DataSelector getDataSelector() { + return clickControl.getClickDataBlock().getDataSelector(getUnitName(), false); + } + + /** + * get the click specific data selector which may now be burried in a + * CompoundDataSelector if annotations have been used. + * @return ClickDataSelector. + */ + public ClickDataSelector getClickDataSelector() { + DataSelector baseSel = getDataSelector(); + if (baseSel == null) { + return null; + } + else { + return (ClickDataSelector) baseSel.findDataSelector(ClickDataSelector.class); + } + } class AmplitudeSelector implements ActionListener { @Override @@ -2795,6 +2842,13 @@ public class ClickBTDisplay extends ClickDisplay implements PamObserver, PamSett showAmplitudeSelector(); } } + + class DataSelectorAction implements ActionListener { + @Override + public void actionPerformed(ActionEvent e) { + showDataSelector(); + } + } private void showAmplitudeSelector() { @@ -2814,6 +2868,38 @@ public class ClickBTDisplay extends ClickDisplay implements PamObserver, PamSett // } } + /** + * Show a dialog with both data and symbol options, but go to the correct tab. + * @param selectedTab + * @return + */ + private boolean showDataSymbolOptions(int selectedTab) { + Window javaFrame = clickControl.getGuiFrame(); + DataSelectDialog dataSelectDialog = new DataSelectDialog(javaFrame, clickControl.getClickDataBlock(), getDataSelector(), symbolChooser); + if (javaFrame == null) { + dataSelectDialog.moveToMouseLocation(); + } + dataSelectDialog.setTab(selectedTab); + boolean ok = dataSelectDialog.showDialog(); + if (ok) { + repaintBoth(); + if (clickControl.getOfflineToolbar() != null) { + clickControl.getOfflineToolbar().displayActivated(clickBTDisplay); + } + } + return ok; + } + + public void showDataSelector() { +// if (getDataSelector().showSelectDialog(clickControl.getGuiFrame())) { +// repaintBoth(); +// if (clickControl.getOfflineToolbar() != null) { +// clickControl.getOfflineToolbar().displayActivated(clickBTDisplay); +// } +// }; + showDataSymbolOptions(0); + } + private void checkBTAmplitudeSelectHisto() { if (btAmplitudeSelector == null) { return; @@ -3036,15 +3122,15 @@ public class ClickBTDisplay extends ClickDisplay implements PamObserver, PamSett zoomer.paintShape(g, this, true); } - // long t1 = System.nanoTime(); - synchronized (clickData.getSynchLock()) { - // long t2 = System.nanoTime(); - // double ms = ((double) (t2-t1)) / 1000000.; - // if (btDisplayParameters.VScale == BTDisplayParameters.DISPLAY_ICI) { - // sortTempICIs(); - // } + ArrayList clickCopy = clickData.getDataCopy(displayStartMillis, displayStartMillis+displayLengthMillis, true, getDataSelector()); + if (clickCopy.size() == 0) { + return; + } + ListIterator clickIterator = clickCopy.listIterator(clickCopy.size()-1); - ListIterator clickIterator = clickData.getListIterator(PamDataBlock.ITERATOR_END); +// synchronized (clickData.getSynchLock()) { +// +// ListIterator clickIterator = clickData.getListIterator(PamDataBlock.ITERATOR_END); while (clickIterator.hasPrevious()) { click = clickIterator.previous(); if (shouldPlot(prevPlottedClick)){ @@ -3060,11 +3146,6 @@ public class ClickBTDisplay extends ClickDisplay implements PamObserver, PamSett if (shouldPlot(prevPlottedClick)){ // and draw the last one ! drawClick(g, prevPlottedClick, clipRectangle); } - // g.drawString(String.format("Wait synch %3.3fms", ms), 0, 20); - } - // long t3 = System.nanoTime(); - // g.drawString(String.format("Last draw %3.3fms", lastPaintTime), 0, 20); - // lastPaintTime = ((double) (t3-t0)) / 1000000.; } @Override @@ -3342,7 +3423,9 @@ public class ClickBTDisplay extends ClickDisplay implements PamObserver, PamSett else if (btDisplayParameters.colourScheme == BTDisplayParameters.COLOUR_BY_HYDROPHONE) { keyPanel.add(new TextKeyItem("Colour by hydrophone")); } - if (btDisplayParameters.getShowSpecies(0)) { + ClickAlarmParameters selectParams = getClickDataSelector().getParams(); +// if (btDisplayParameters.getShowSpecies(0)) { + if (selectParams.onlineAutoEvents | selectParams.onlineManualEvents) { keyPanel.add(symbolChooser.getDefaultSymbol(true).makeKeyItem("Unidentified species")); } @@ -3354,7 +3437,8 @@ public class ClickBTDisplay extends ClickDisplay implements PamObserver, PamSett PamSymbol[] symbols = clickControl.getClickIdentifier().getSymbols(); if (speciesList != null) { for (int i = 0; i < speciesList.length; i++) { - if (btDisplayParameters.getShowSpecies(i+1)) { + if (selectParams.getUseSpecies(i+1)) { +// if (btDisplayParameters.getShowSpecies(i+1)) { if (btDisplayParameters.colourScheme != BTDisplayParameters.COLOUR_BY_TRAIN) { keyPanel.add(symbols[i].makeKeyItem(speciesList[i])); } @@ -3423,11 +3507,12 @@ public class ClickBTDisplay extends ClickDisplay implements PamObserver, PamSett PamDataBlock clickData = clickControl.getClickDataBlock(); ClickDetection click; int n = 0; + double minAmpli = getClickDataSelector().getParams().minimumAmplitude; synchronized (clickData.getSynchLock()) { ListIterator clickIterator = clickData.getListIterator(0); while (clickIterator.hasNext()) { click = clickIterator.next(); - if (click.getAmplitudeDB() < btDisplayParameters.minAmplitude) { + if (click.getAmplitudeDB() < minAmpli) { n++; } } @@ -3448,29 +3533,30 @@ public class ClickBTDisplay extends ClickDisplay implements PamObserver, PamSett */ synchronized boolean shouldPlot(ClickDetection click) { if (click == null) return false; - if (!clickInTimeWindow(click)) return false; - if (btDisplayParameters.showEchoes == false && click.isEcho()) { - return false; - } +// if (!clickInTimeWindow(click)) return false; +// if (btDisplayParameters.showEchoes == false && click.isEcho()) { +// return false; +// } if (btDisplayParameters.VScale == BTDisplayParameters.DISPLAY_ICI) { // if (btDisplayParameters.showUnassignedICI == false && click.getICI() < 0) return false; if (btDisplayParameters.showUnassignedICI == false && click.getSuperDetectionsCount() <= 0) return false; // otherwise may be ok, since will estimate all ici's on teh fly. } - if (btDisplayParameters.amplitudeSelect && click.getAmplitudeDB() < btDisplayParameters.minAmplitude) { - return false; - } +// if (btDisplayParameters.amplitudeSelect && click.getAmplitudeDB() < btDisplayParameters.minAmplitude) { +// return false; +// } if (btDisplayParameters.displayChannels > 0 && (btDisplayParameters.displayChannels & click.getChannelBitmap()) == 0) return false; - int speciesIndex = clickControl.getClickIdentifier().codeToListIndex(click.getClickType()); - boolean showSpecies = btDisplayParameters.getShowSpecies(speciesIndex+1); - boolean showEvents = (btDisplayParameters.showEventsOnly == false || click.getSuperDetectionsCount() > 0); - if (btDisplayParameters.showANDEvents) { - return showSpecies & showEvents; - } - else { - return showSpecies | showEvents; - } +// int speciesIndex = clickControl.getClickIdentifier().codeToListIndex(click.getClickType()); +// boolean showSpecies = btDisplayParameters.getShowSpecies(speciesIndex+1); +// boolean showEvents = (btDisplayParameters.showEventsOnly == false || click.getSuperDetectionsCount() > 0); +// if (btDisplayParameters.showANDEvents) { +// return showSpecies & showEvents; +// } +// else { +// return showSpecies | showEvents; +// } + return true; } /** @@ -4085,7 +4171,7 @@ public class ClickBTDisplay extends ClickDisplay implements PamObserver, PamSett amplitudeSelectorLabel.setText(""); } else { - String txt = String.format(" Amplitude Selector showing clicks > %3.1fdB", btDisplayParameters.minAmplitude); + String txt = String.format(" Amplitude Selector showing clicks > %3.1fdB", getClickDataSelector().getParams().minimumAmplitude); amplitudeSelectorLabel.setText(txt); } } diff --git a/src/clickDetector/alarm/ClickAlarmParameters.java b/src/clickDetector/alarm/ClickAlarmParameters.java index 8bed53c6..d126f4c6 100644 --- a/src/clickDetector/alarm/ClickAlarmParameters.java +++ b/src/clickDetector/alarm/ClickAlarmParameters.java @@ -17,10 +17,12 @@ public class ClickAlarmParameters extends DataSelectParams implements Cloneable, public static final long serialVersionUID = 1L; private boolean[] useSpeciesList; private double[] speciesWeightings; - public boolean useEchoes; - public boolean scoreByAmplitude; - public boolean onlineAutoEvents, onlineManualEvents; + public boolean useEchoes = true; + public boolean scoreByAmplitude; // alarm options, probably not used any more. + public double minimumAmplitude; + public boolean onlineAutoEvents = true, onlineManualEvents = true; public int minICIMillis; + private boolean clicksOREvents = false; /* * Which events to use ... */ @@ -165,5 +167,30 @@ public class ClickAlarmParameters extends DataSelectParams implements Cloneable, return ps; } + /** + * @return the clicksOREvents + */ + public boolean isClicksOREvents() { + return clicksOREvents; + } + + /** + * @param clicksOREvents the clicksOREvents to set + */ + public void setClicksOREvents(boolean clicksOREvents) { + this.clicksOREvents = clicksOREvents; + } + /** + * @return the clicksANDEvents + */ + public boolean isClicksANDEvents() { + return !clicksOREvents; + } + + + public void setClicksANDEvents(boolean clicksANDEvents) { + this.clicksOREvents = !clicksANDEvents; + } + } diff --git a/src/clickDetector/dataSelector/ClickDataSelector.java b/src/clickDetector/dataSelector/ClickDataSelector.java index 5be77c2a..9f2248d3 100644 --- a/src/clickDetector/dataSelector/ClickDataSelector.java +++ b/src/clickDetector/dataSelector/ClickDataSelector.java @@ -38,9 +38,9 @@ public class ClickDataSelector extends DataSelector { @Override public PamDialogPanel getDialogPanel() { - if (clickSelectPanel == null) { +// if (clickSelectPanel == null) { clickSelectPanel = new ClickSelectPanel(this, allowScores, useEventTypes); - } +// } return clickSelectPanel; } @Override @@ -60,19 +60,25 @@ public class ClickDataSelector extends DataSelector { if (clickAlarmParameters.useEchoes == false && click.isEcho()) { return 0; } - /** - * First score based on whether the event panel is in use and - * criteria satisfied. - */ + + double score = scoreClick(click); if (useEventTypes) { - if (wantEventType(click) == false) { - return 0; + double eventScore = scoreEventType(click); + if (clickAlarmParameters.isClicksANDEvents()) { + score = Math.min(score, eventScore); + } + else { + score = Math.max(score, eventScore); } } + return score; - /* - * Now score based on whether or not it's individual click type is wanted. - */ + } + + private double scoreClick(ClickDetection click) { + if (click.getAmplitudeDB() < clickAlarmParameters.minimumAmplitude) { + return 0; + } ClickIdentifier clickIdentifier = clickControl.getClickIdentifier(); int code = click.getClickType(); if (code > 0 && clickIdentifier != null) { @@ -81,13 +87,13 @@ public class ClickDataSelector extends DataSelector { boolean enabled = clickAlarmParameters.getUseSpecies(code); if (enabled == false) { return 0; - } - if (isAllowScores()) { + }if (isAllowScores()) { return clickAlarmParameters.getSpeciesWeight(code); } else { return 1; } + } /** @@ -95,7 +101,7 @@ public class ClickDataSelector extends DataSelector { * @param click * @return */ - private boolean wantEventType(ClickDetection click) { + private double scoreEventType(ClickDetection click) { OfflineEventDataUnit oev = null; try { @@ -107,7 +113,7 @@ public class ClickDataSelector extends DataSelector { int eventId = click.getOfflineEventID(); if (oev == null) { - return clickAlarmParameters.unassignedEvents; + return clickAlarmParameters.unassignedEvents ? 1 : 0; } // see if there is a super detection and see if it's got a comment. @@ -118,10 +124,10 @@ public class ClickDataSelector extends DataSelector { isAutomatic = comment.startsWith("Automatic"); } if (isAutomatic && clickAlarmParameters.onlineAutoEvents) { - return true; + return 1; } else if (clickAlarmParameters.onlineManualEvents) { - return true; + return 1; } // if (clickAlarmParameters.onlineAutoEvents && comment.startsWith("Automatic")) { // return true; @@ -134,7 +140,7 @@ public class ClickDataSelector extends DataSelector { * list of event types and see if it's wanted. */ String evType = oev.getEventType(); - return clickAlarmParameters.isUseEventType(evType); + return clickAlarmParameters.isUseEventType(evType) ? 1 : 0; } @@ -188,7 +194,7 @@ public class ClickDataSelector extends DataSelector { * @see PamguardMVC.dataSelector.DataSelector#getParams() */ @Override - public DataSelectParams getParams() { + public ClickAlarmParameters getParams() { return clickAlarmParameters; } diff --git a/src/clickDetector/dataSelector/ClickSelectPaneFX.java b/src/clickDetector/dataSelector/ClickSelectPaneFX.java index 0c1d661d..04e911ff 100644 --- a/src/clickDetector/dataSelector/ClickSelectPaneFX.java +++ b/src/clickDetector/dataSelector/ClickSelectPaneFX.java @@ -95,7 +95,7 @@ public class ClickSelectPaneFX extends DynamicSettingsPane { @Override public String getName() { - return "Click Type Selection"; + return "Filter by click type"; } @Override diff --git a/src/clickDetector/dataSelector/ClickSelectPanel.java b/src/clickDetector/dataSelector/ClickSelectPanel.java index 512e088d..60e1566a 100644 --- a/src/clickDetector/dataSelector/ClickSelectPanel.java +++ b/src/clickDetector/dataSelector/ClickSelectPanel.java @@ -4,6 +4,7 @@ import generalDatabase.lookupTables.LookUpTables; import generalDatabase.lookupTables.LookupList; import java.awt.BorderLayout; +import java.awt.Dimension; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; @@ -13,11 +14,13 @@ import java.awt.event.ActionListener; import javax.swing.BoxLayout; import javax.swing.JButton; import javax.swing.JCheckBox; +import javax.swing.JComboBox; import javax.swing.JComponent; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JScrollPane; import javax.swing.JTextField; +import javax.swing.border.EmptyBorder; import javax.swing.border.TitledBorder; import PamController.PamController; @@ -42,6 +45,7 @@ public class ClickSelectPanel implements PamDialogPanel { private ClickDataSelector clickDataSelector; private JPanel mainPanel; private boolean isViewer; + private JComboBox andOrBox; public static final String mainTip = "You should select options in both the Click Type and the Event Type panels"; @@ -70,10 +74,12 @@ public class ClickSelectPanel implements PamDialogPanel { public void setParams() { eventTypePanel.setParams(); speciesPanel.setParams(); + andOrBox.setSelectedIndex(clickDataSelector.getParams().isClicksANDEvents() ? 0 : 1); } @Override public boolean getParams() { + clickDataSelector.getParams().setClicksANDEvents(andOrBox.getSelectedIndex() == 0); return (speciesPanel.getParams() & eventTypePanel.getParams()); } @@ -163,17 +169,26 @@ public class ClickSelectPanel implements PamDialogPanel { // JRadioButton andEvents, orEvents; // JRadioButton anyEvents, onlyEvents; private JCheckBox useEchoes; + private JTextField minAmplitude; private JCheckBox scoreByAmplitude; private JTextField minICI; SpeciesPanel () { super(); - setLayout(new BorderLayout()); +// setLayout(new BorderLayout()); + setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); northPanel = new JPanel(); northPanel.setLayout(new GridBagLayout()); GridBagConstraints c = new PamGridBagContraints(); - c.gridwidth = 3; + c.gridwidth = 1; c.anchor = GridBagConstraints.WEST; + northPanel.add(new JLabel("Minimum amplitude ", JLabel.RIGHT), c); + c.gridx++; + northPanel.add(minAmplitude = new JTextField(4), c); + c.gridx++; + northPanel.add(new JLabel(" dB")); + c.gridx = 0; + c.gridy++; northPanel.add(new PamAlignmentPanel(useEchoes = new JCheckBox("Use Echoes"), BorderLayout.WEST), c); c.gridwidth = 1; c.gridy++; @@ -185,35 +200,50 @@ public class ClickSelectPanel implements PamDialogPanel { // minICI.setToolTipText("Minimum ICI in milliseconds"); // c.gridx++; // northPanel.add(new JLabel(" ms", JLabel.LEFT), c); - c.gridwidth = 3; - c.gridy++; - c.gridx = 0; - northPanel.add(scoreByAmplitude = new JCheckBox("Score by amplitude"), c); - scoreByAmplitude.setVisible(allowScores); - scoreByAmplitude.addActionListener(new AllSpeciesListener()); + scoreByAmplitude = new JCheckBox("Score by amplitude"); + if (allowScores) { + c.gridwidth = 3; + c.gridy++; + c.gridx = 0; + northPanel.add(scoreByAmplitude, c); + scoreByAmplitude.setVisible(allowScores); + scoreByAmplitude.addActionListener(new AllSpeciesListener()); + } WestAlignedPanel walpn; - add(BorderLayout.NORTH, walpn = new WestAlignedPanel(northPanel)); - walpn.setBorder(new SeparatorBorder("Echoes")); + this.add(walpn = new WestAlignedPanel(northPanel)); + walpn.setBorder(new SeparatorBorder("Click Selection")); JPanel centralOuterPanel = new JPanel(new BorderLayout()); centralPanel.setLayout(new GridBagLayout()); - centralOuterPanel.setBorder(new SeparatorBorder("Click Type Selection")); + centralOuterPanel.setBorder(new SeparatorBorder("Click Types")); - add(BorderLayout.CENTER, centralOuterPanel); + this.add(centralOuterPanel); JScrollPane scrollPane = new DialogScrollPane(new PamAlignmentPanel(centralPanel, BorderLayout.WEST), 10); centralOuterPanel.add(BorderLayout.CENTER, scrollPane); centralEastPanel.setLayout(new GridBagLayout()); c = new PamGridBagContraints(); - centralEastPanel.add(selectAll = new JButton("Select All"), c); - c.gridx++; - centralEastPanel.add(clearAll = new JButton("Clear All"), c); + c.ipady = 0; + c.insets.bottom = c.insets.top = c.insets.left = c.insets.right = 0; + centralEastPanel.add(selectAll = new JButton("All"), c); + c.gridy++; + centralEastPanel.add(clearAll = new JButton("None"), c); + selectAll.setBorder(new EmptyBorder(3,3,2,3)); + clearAll.setBorder(new EmptyBorder(3,3,2,3)); selectAll.addActionListener(new AutoSelect(true)); clearAll.addActionListener(new AutoSelect(false)); - centralOuterPanel.add(BorderLayout.SOUTH, new PamAlignmentPanel(centralEastPanel, BorderLayout.WEST)); + centralOuterPanel.add(BorderLayout.EAST, new PamAlignmentPanel(centralEastPanel, BorderLayout.NORTH)); centralOuterPanel.setToolTipText(mainTip); + this.add(andOrBox = new JComboBox<>()); + andOrBox.setToolTipText("Select how to logically combine the click and event selections"); + andOrBox.addItem("AND"); + andOrBox.addItem("OR"); + JPanel emptyPanel = new JPanel(); + emptyPanel.setPreferredSize(new Dimension(10, 5)); + this.add(emptyPanel); + setToolTipText(mainTip); } @@ -254,6 +284,7 @@ public class ClickSelectPanel implements PamDialogPanel { } } useEchoes.setSelected(clickAlarmParameters.useEchoes); + minAmplitude.setText(String.format("%3.1f", clickAlarmParameters.minimumAmplitude)); minICI.setText(String.format("%d", clickAlarmParameters.minICIMillis)); scoreByAmplitude.setSelected(clickAlarmParameters.scoreByAmplitude); allWeight.setText(String.format("%3.1f", clickAlarmParameters.getSpeciesWeight(0))); @@ -279,6 +310,12 @@ public class ClickSelectPanel implements PamDialogPanel { ClickAlarmParameters clickAlarmParameters = clickDataSelector.getClickAlarmParameters().clone(); clickAlarmParameters.useEchoes = useEchoes.isSelected(); + try { + clickAlarmParameters.minimumAmplitude = Double.valueOf(minAmplitude.getText()); + } + catch (NumberFormatException e) { + return PamDialog.showWarning(null, "Minimum amplitude", "Invalid minimum amplitude value"); + } try { clickAlarmParameters.minICIMillis = Integer.valueOf(minICI.getText()); } diff --git a/src/clickDetector/dialogs/ClickDisplayDialog.java b/src/clickDetector/dialogs/ClickDisplayDialog.java index 0a049c30..5c08f7bd 100644 --- a/src/clickDetector/dialogs/ClickDisplayDialog.java +++ b/src/clickDetector/dialogs/ClickDisplayDialog.java @@ -31,6 +31,7 @@ import PamView.dialog.warn.WarnOnce; import clickDetector.BTDisplayParameters; import clickDetector.ClickControl; import clickDetector.ClickClassifiers.ClickIdentifier; +import clickDetector.alarm.ClickAlarmParameters; /** * Dialog for basic click display parameters @@ -57,6 +58,8 @@ public class ClickDisplayDialog extends PamDialog implements ActionListener { private SizePanel sizePanel; private SpeciesPanel speciesPanel; private JComboBox angleTypes; + + private ClickAlarmParameters clickSelectParams; private ClickDisplayDialog(Window owner) { @@ -86,12 +89,13 @@ public class ClickDisplayDialog extends PamDialog implements ActionListener { } - public static BTDisplayParameters showDialog(ClickControl clickControl, Window parentFrame, BTDisplayParameters btDisplayParameters) { + public static BTDisplayParameters showDialog(ClickControl clickControl, Window parentFrame, BTDisplayParameters btDisplayParameters, ClickAlarmParameters clickSelectParams) { if (singleInstance == null || singleInstance.getOwner() != parentFrame) { singleInstance = new ClickDisplayDialog(parentFrame); } singleInstance.clickControl = clickControl; singleInstance.btDisplayParameters = btDisplayParameters.clone(); + singleInstance.clickSelectParams = clickSelectParams; singleInstance.setParams(btDisplayParameters); singleInstance.setVisible(true); return singleInstance.btDisplayParameters; @@ -514,14 +518,16 @@ public class ClickDisplayDialog extends PamDialog implements ActionListener { } } } - showEchoes.setSelected(btDisplayParameters.showEchoes); + showEchoes.setSelected(clickSelectParams.useEchoes); if (species == null) { showAll.setSelected(true); } else { - showAll.setSelected(btDisplayParameters.getShowSpecies(0)); +// showAll.setSelected(btDisplayParameters.getShowSpecies(0)); + showAll.setSelected(clickSelectParams.getUseSpecies(0)); for (int i = 0; i < species.length; i++) { - species[i].setSelected(btDisplayParameters.getShowSpecies(i+1)); + species[i].setSelected(clickSelectParams.getUseSpecies(i+1)); +// species[i].setSelected(btDisplayParameters.getShowSpecies(i+1)); } // if (btDisplayParameters.showSpeciesList != null) { // for (int i = 0; i < Math.min(species.length, btDisplayParameters.showSpeciesList.length);i++) { @@ -529,9 +535,10 @@ public class ClickDisplayDialog extends PamDialog implements ActionListener { // } // } } - - clicksInAnEvent.setSelected(btDisplayParameters.showEventsOnly); - andOrSelection.setSelectedIndex(btDisplayParameters.showANDEvents ? 0: 1); + clicksInAnEvent.setSelected(clickSelectParams.onlineAutoEvents | clickSelectParams.onlineAutoEvents); + +// clicksInAnEvent.setSelected(btDisplayParameters.showEventsOnly); +// andOrSelection.setSelectedIndex(btDisplayParameters.showANDEvents ? 0: 1); // orEvents.setSelected(!btDisplayParameters.showANDEvents); // andEvents.setSelected(btDisplayParameters.showANDEvents); // anyEvents.setSelected(!btDisplayParameters.showEventsOnly); @@ -540,19 +547,17 @@ public class ClickDisplayDialog extends PamDialog implements ActionListener { enableButtons(); } boolean getParams() { - btDisplayParameters.showEchoes = showEchoes.isSelected(); - btDisplayParameters.setShowSpecies(0, showAll.isSelected()); - if (species != null) { - for (int i = 0; i < species.length; i++) { - btDisplayParameters.setShowSpecies(i+1, species[i].isSelected()); - } - - } - - btDisplayParameters.showEventsOnly = clicksInAnEvent.isSelected(); - btDisplayParameters.showANDEvents = (andOrSelection.getSelectedIndex() == 0); -// btDisplayParameters.showANDEvents = andEvents.isSelected(); -// btDisplayParameters.showEventsOnly = onlyEvents.isSelected(); +// clickSelectParams.useEchoes = showEchoes.isSelected(); +// btDisplayParameters.setShowSpecies(0, showAll.isSelected()); +// if (species != null) { +// for (int i = 0; i < species.length; i++) { +// btDisplayParameters.setShowSpecies(i+1, species[i].isSelected()); +// } +// +// } +// +// btDisplayParameters.showEventsOnly = clicksInAnEvent.isSelected(); +// btDisplayParameters.showANDEvents = (andOrSelection.getSelectedIndex() == 0); return true; } class AllSpeciesListener implements ActionListener { diff --git a/src/clickDetector/offlineFuncs/OfflineToolbar.java b/src/clickDetector/offlineFuncs/OfflineToolbar.java index 9ff5ba5f..f7e1e34c 100644 --- a/src/clickDetector/offlineFuncs/OfflineToolbar.java +++ b/src/clickDetector/offlineFuncs/OfflineToolbar.java @@ -31,12 +31,15 @@ import clickDetector.ClickBTDisplay; import clickDetector.ClickControl; import clickDetector.ClickDisplay; import clickDetector.ClickClassifiers.ClickIdentifier; +import clickDetector.alarm.ClickAlarmParameters; +import clickDetector.dataSelector.ClickDataSelector; import PamView.PamToolBar; import PamView.component.PamSettingsIconButton; import PamView.dialog.PamCheckBox; import PamView.dialog.PamLabel; import PamView.dialog.PamRadioButton; import PamView.panel.PamPanel; +import PamguardMVC.dataSelector.DataSelectParams; public class OfflineToolbar { @@ -252,19 +255,29 @@ public class OfflineToolbar { return; } try { + ClickDataSelector clickDataSelector = currentBTDisplay.getClickDataSelector(); + ClickAlarmParameters selectParams = clickDataSelector.getParams(); BTDisplayParameters btDisplayParameters = currentBTDisplay.getBtDisplayParameters(); - btDisplayParameters.setShowSpecies(0, showNonSpecies.isSelected()); - btDisplayParameters.showEchoes = showEchoes.isSelected(); + + selectParams.setUseSpecies(0, showNonSpecies.isSelected()); +// btDisplayParameters.setShowSpecies(0, showNonSpecies.isSelected()); +// btDisplayParameters.showEchoes = showEchoes.isSelected(); + selectParams.useEchoes = showEchoes.isSelected(); + if (clicksInAnEvent != null) { - btDisplayParameters.showEventsOnly = clicksInAnEvent.isSelected(); +// btDisplayParameters.showEventsOnly = clicksInAnEvent.isSelected(); +// selectParams.onlineAutoEvents = selectParams.onlineManualEvents = true; + selectParams.unassignedEvents =clicksInAnEvent.isSelected() == false; } if (andOrSelection != null) { - btDisplayParameters.showANDEvents = (andOrSelection.getSelectedIndex() == 0); + selectParams.setClicksANDEvents(andOrSelection.getSelectedIndex() == 0); +// btDisplayParameters.showANDEvents = (andOrSelection.getSelectedIndex() == 0); } if (speciesButtons != null) { int n = speciesButtons.length; for (int i = 0; i < n; i++) { - btDisplayParameters.setShowSpecies(i+1, speciesButtons[i].isSelected()); + selectParams.setUseSpecies(i+1, speciesButtons[i].isSelected()); +// btDisplayParameters.setShowSpecies(i+1, speciesButtons[i].isSelected()); } } currentBTDisplay.repaintTotal(); @@ -275,23 +288,35 @@ public class OfflineToolbar { } private void checkButtons(BTDisplayParameters btDisplayParameters) { - showEchoes.setSelected(btDisplayParameters.showEchoes); - showNonSpecies.setSelected(btDisplayParameters.getShowSpecies(0)); + ClickDataSelector clickDataSelector = currentBTDisplay.getClickDataSelector(); + ClickAlarmParameters selectParams = clickDataSelector.getParams(); + showEchoes.setSelected(selectParams.useEchoes); + showNonSpecies.setSelected(selectParams.getUseSpecies(0)); +// showNonSpecies.setSelected(btDisplayParameters.getShowSpecies(0)); + boolean anySel = false;; if (clicksInAnEvent != null) { - clicksInAnEvent.setSelected(btDisplayParameters.showEventsOnly); + clicksInAnEvent.setSelected(selectParams.unassignedEvents == false); + anySel |= clicksInAnEvent.isSelected(); +// clicksInAnEvent.setSelected(btDisplayParameters.showEventsOnly); } if (speciesButtons != null) { int n = speciesButtons.length; for (int i = 0; i < n; i++) { - speciesButtons[i].setSelected(btDisplayParameters.getShowSpecies(i+1)); + speciesButtons[i].setSelected(selectParams.getUseSpecies(i+1)); + anySel |= speciesButtons[i].isSelected(); +// speciesButtons[i].setSelected(btDisplayParameters.getShowSpecies(i+1)); } } // setting combo box fires actionlistener, so we have to make sure that all checkboxes have been properly set first // or else they will get cleared later if (andOrSelection != null) { - andOrSelection.setSelectedIndex(btDisplayParameters.showANDEvents ? 0: 1); + andOrSelection.setSelectedIndex(selectParams.isClicksANDEvents() ? 0 : 1); +// andOrSelection.setSelectedIndex(btDisplayParameters.showANDEvents ? 0: 1); } firstSetup = true; + if (anySel) { + selectParams.setCombinationFlag(DataSelectParams.DATA_SELECT_AND); + } } } diff --git a/src/clickDetector/tdPlots/ClickClassSymbolModifier.java b/src/clickDetector/tdPlots/ClickClassSymbolModifier.java index 2ad9c3c7..e27b9a4a 100644 --- a/src/clickDetector/tdPlots/ClickClassSymbolModifier.java +++ b/src/clickDetector/tdPlots/ClickClassSymbolModifier.java @@ -1,7 +1,13 @@ package clickDetector.tdPlots; +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JPanel; +import javax.swing.border.TitledBorder; + import PamView.GeneralProjector; import PamView.PamSymbol; +import PamView.dialog.PamDialogPanel; import PamView.symbol.PamSymbolChooser; import PamView.symbol.SymbolData; import PamView.symbol.modifier.SymbolModType; @@ -32,4 +38,41 @@ public class ClickClassSymbolModifier extends SymbolModifier { } } + @Override + public PamDialogPanel getDialogPanel() { + // Just a play to check buttons and menus work. Not actually used at all so revert to super: returns null. + return super.getDialogPanel(); +// return new DumyPanel(); + } + + private class DumyPanel implements PamDialogPanel { + + private JPanel mainPanel; + public DumyPanel() { + super(); + mainPanel = new JPanel(); + mainPanel.setBorder(new TitledBorder("More options")); + mainPanel.add(new JCheckBox("Dummy option")); + } + + @Override + public JComponent getDialogComponent() { + // TODO Auto-generated method stub + return mainPanel; + } + + @Override + public void setParams() { + // TODO Auto-generated method stub + + } + + @Override + public boolean getParams() { + // TODO Auto-generated method stub + return false; + } + + } + } diff --git a/src/clickDetector/tdPlots/ClickPlotInfo.java b/src/clickDetector/tdPlots/ClickPlotInfo.java index 18cdfb7a..baa60626 100644 --- a/src/clickDetector/tdPlots/ClickPlotInfo.java +++ b/src/clickDetector/tdPlots/ClickPlotInfo.java @@ -9,6 +9,8 @@ import clickDetector.BTDisplayParameters; import clickDetector.ClickControl; import clickDetector.ClickDetection; import clickDetector.ClickDisplayManagerParameters; +import clickDetector.alarm.ClickAlarmParameters; +import clickDetector.dataSelector.ClickDataSelector; import clickDetector.dialogs.ClickDisplayDialog; import PamView.GeneralProjector.ParameterType; import PamView.GeneralProjector.ParameterUnits; @@ -52,6 +54,10 @@ public class ClickPlotInfo extends TDDataInfo { allScaleInfo[3] = slantScaleInfo; clickHidingDialog = new ClickHidingDialog(this); } + + ClickDataSelector getDataSelector() { + return (ClickDataSelector) clickControl.getClickDataBlock().getDataSelector("ClickTDPlots", false); + } @Override public Double getDataValue(PamDataUnit pamDataUnit) { @@ -110,7 +116,8 @@ public class ClickPlotInfo extends TDDataInfo { private synchronized boolean shouldPlot(ClickDetection click) { if (click == null) return false; - if (btDisplayParams.showEchoes == false && click.isEcho()) { + boolean showEchoes = getDataSelector().getClickAlarmParameters().useEchoes; + if (showEchoes == false && click.isEcho()) { return false; } @@ -170,7 +177,7 @@ public class ClickPlotInfo extends TDDataInfo { */ @Override public boolean editOptions(Window frame) { - BTDisplayParameters newParams = ClickDisplayDialog.showDialog(clickControl, frame, btDisplayParams); + BTDisplayParameters newParams = ClickDisplayDialog.showDialog(clickControl, frame, btDisplayParams, getDataSelector().getClickAlarmParameters()); if (newParams != null) { btDisplayParams = newParams.clone(); updateSettings(); diff --git a/src/clickTrainDetector/layout/classification/templateClassifier/TemplateSpectrumPane.java b/src/clickTrainDetector/layout/classification/templateClassifier/TemplateSpectrumPane.java index ab4af7c6..b7d2d5d3 100644 --- a/src/clickTrainDetector/layout/classification/templateClassifier/TemplateSpectrumPane.java +++ b/src/clickTrainDetector/layout/classification/templateClassifier/TemplateSpectrumPane.java @@ -185,6 +185,7 @@ public class TemplateSpectrumPane extends PamBorderPane { this.currentSpectrum = new SpectrumTemplateDataUnit(spectrumTemplate); templateDisplay.setDataUnit(currentSpectrum); + } /** @@ -226,7 +227,6 @@ public class TemplateSpectrumPane extends PamBorderPane { if (templateImporters.get(i).getExtension()[j].equals(extension)) { // System.out.println("Import using the extensions: " + extension); template=templateImporters.get(i).importTemplate(file); - } } } @@ -288,7 +288,7 @@ public class TemplateSpectrumPane extends PamBorderPane { } @Override - public double[][] getPowerSpectrum(SpectrumTemplateDataUnit data, int strat, int end) { + public double[][] getPowerSpectrum(SpectrumTemplateDataUnit data, int start, int end) { return new double[][]{data.spectrumTemplate.waveform}; } @@ -296,6 +296,7 @@ public class TemplateSpectrumPane extends PamBorderPane { @Override public double getSampleRate(SpectrumTemplateDataUnit data) { //bit of a hack to get sample rate but works. +// System.out.println("Set sample rate: " + data.spectrumTemplate.sR); return data.spectrumTemplate.sR; } diff --git a/src/dataPlotsFX/clickPlotFX/ClickControlPane2.java b/src/dataPlotsFX/clickPlotFX/ClickControlPane2.java index ad31f10c..225fc098 100644 --- a/src/dataPlotsFX/clickPlotFX/ClickControlPane2.java +++ b/src/dataPlotsFX/clickPlotFX/ClickControlPane2.java @@ -189,11 +189,6 @@ public class ClickControlPane2 extends PamBorderPane implements TDSettingsPane { dataSelectHolder.setPadding(new Insets(5,5,5,5)); dataSelectPane = createDataSelectPane(); - dataSelectPane.addSettingsListener(()->{ - //dynamic settings pane so have to repaint whenever a control is selected. - getParams(); - clickPlotInfo.getTDGraph().repaint(0); - }); dataSelectPane.setParams(true); @@ -215,6 +210,7 @@ public class ClickControlPane2 extends PamBorderPane implements TDSettingsPane { scrollPane2.setHbarPolicy(ScrollBarPolicy.NEVER); scrollPane2.getStyleClass().clear(); symbolTab.setContent(scrollPane2); + Tab dataView=new Tab("Data"); dataView.setContent(dataSelectHolder); dataView.getStyleClass().add("tab-square"); @@ -230,6 +226,21 @@ public class ClickControlPane2 extends PamBorderPane implements TDSettingsPane { enablePane(); setFreqType(); setParams(); + + dataSelectPane.addSettingsListener(()->{ + //dynamic settings pane so have to repaint whenever a control is selected. + getParams(); + + /** + * If there are raw amplitude or frequency panes that have a buffer of painted units then + * these have to be cleared for the data selector + */ + clickPlotInfo.getClickRawPlotManager().clear(); + clickPlotInfo.getClickFFTPlotManager().clear(); + + + clickPlotInfo.getTDGraph().repaint(50); + }); this.setCenter(tabPane); @@ -242,6 +253,7 @@ public class ClickControlPane2 extends PamBorderPane implements TDSettingsPane { * @return the data select pane. */ private DynamicSettingsPane createDataSelectPane(){ +// System.out.println("DATA SELECTOR: " + clickPlotInfo.getClickDataSelector()); return clickPlotInfo.getClickDataSelector().getDialogPaneFX(); } @@ -446,7 +458,7 @@ public class ClickControlPane2 extends PamBorderPane implements TDSettingsPane { PamVBox vBox = new PamVBox(); vBox.setSpacing(5); - minMaxWidthPane = new DualControlField("Min", "Max" , "", 2, 100, 1); + minMaxWidthPane = new DualControlField("", "" , "", 2, 100, 1); minMaxWidthPane.addChangeListener((obsval, oldval, newval)->{ newSettings(); //do not allow the min ti be larger than the max. @@ -469,7 +481,7 @@ public class ClickControlPane2 extends PamBorderPane implements TDSettingsPane { //height pane - minMaxHeightPane = new DualControlField("Min", "Max" , "", 2, 100, 1); + minMaxHeightPane = new DualControlField("", "" , "", 2, 100, 1); minMaxHeightPane.addChangeListener((obsval, oldval, newval)->{ newSettings(); //do not allow the min ti be larger than the max. @@ -516,7 +528,7 @@ public class ClickControlPane2 extends PamBorderPane implements TDSettingsPane { getParams(); //on a parameter change must clear the FFT plot. - clickPlotInfo.getClickFFTplotManager().clear(); + clickPlotInfo.getClickFFTPlotManager().clear(); clickPlotInfo.getClickRawPlotManager().clear(); clickPlotInfo.getTDGraph().repaint(milliswait); @@ -770,7 +782,7 @@ public class ClickControlPane2 extends PamBorderPane implements TDSettingsPane { clickPlotInfo.getClickDisplayParams().fftHop = this.fftSpinnerHop.getValue().intValue(); clickPlotInfo.getClickDisplayParams().fftLength = this.fftSpinnerLength.getValue().intValue(); clickPlotInfo.getClickDisplayParams().colourMap = this.spectroControlPane.getColourArrayType(); - clickPlotInfo.getClickFFTplotManager().update(); + clickPlotInfo.getClickFFTPlotManager().update(); /*** Data select pane****/ //dynamic settings pane so have to repaint whenever a control is selected. diff --git a/src/dataPlotsFX/clickPlotFX/ClickPlotInfoFX.java b/src/dataPlotsFX/clickPlotFX/ClickPlotInfoFX.java index 59924c42..b13a85d8 100644 --- a/src/dataPlotsFX/clickPlotFX/ClickPlotInfoFX.java +++ b/src/dataPlotsFX/clickPlotFX/ClickPlotInfoFX.java @@ -238,7 +238,7 @@ public class ClickPlotInfoFX extends TDDataInfoFX { ClickDetection click = (ClickDetection) pamDataUnit; //first check we can generally plot the click - if (!shouldPlot(click)) return null; +// if (!shouldPlot(click)) return null; //click has passed the first test! Now get the correct data value; Double val = null; switch (getCurrentScaleInfo().getDataType()) { @@ -599,21 +599,21 @@ public class ClickPlotInfoFX extends TDDataInfoFX { // } - /*** - * Used to determine which clicks should be plotted on the display by using the click data selector. - * @param click - the click to check. - * @return true to plot the click. - */ - private synchronized boolean shouldPlot(ClickDetection click) { - - if (click == null) return false; - - dataSelector.scoreData(click); - - if (clickDisplayParams.displayChannels > 0 && (clickDisplayParams.displayChannels & click.getChannelBitmap()) == 0) return false; - - return true; - } +// /*** +// * Used to determine which clicks should be plotted on the display by using the click data selector. +// * @param click - the click to check. +// * @return true to plot the click. +// */ +// private synchronized boolean shouldPlot(ClickDetection click) { +// +// if (click == null) return false; +// +// if (dataSelector.scoreData(click)<=0) return false; +// +// if (clickDisplayParams.displayChannels > 0 && (clickDisplayParams.displayChannels & click.getChannelBitmap()) == 0) return false; +// +// return true; +// } @Override public TDSymbolChooserFX getSymbolChooser() { @@ -931,7 +931,7 @@ public class ClickPlotInfoFX extends TDDataInfoFX { * time/frequency displays... * @return The FFT plot manager. */ - public ClickFFTPlotManager2 getClickFFTplotManager() { + public ClickFFTPlotManager2 getClickFFTPlotManager() { return clickFFTplotManager; } diff --git a/src/dataPlotsFX/data/RawWavePlotManager.java b/src/dataPlotsFX/data/RawWavePlotManager.java index b4b873e6..79b8ef35 100644 --- a/src/dataPlotsFX/data/RawWavePlotManager.java +++ b/src/dataPlotsFX/data/RawWavePlotManager.java @@ -318,7 +318,6 @@ public abstract class RawWavePlotManager { //check if we can plot click on this plot pane. if (!rawClipInfo.shouldDraw(plotNumber, pamDataUnit)) { //System.out.println("Shoudl not draw!"); - return null; } diff --git a/src/dataPlotsFX/layout/TDGraphFX.java b/src/dataPlotsFX/layout/TDGraphFX.java index a0fa1dc5..b3ca0514 100644 --- a/src/dataPlotsFX/layout/TDGraphFX.java +++ b/src/dataPlotsFX/layout/TDGraphFX.java @@ -901,13 +901,14 @@ public class TDGraphFX extends PamBorderPane { } /** - * A tgool tip for detections. + * A Tooltip for showing information about detections. * * @author Jamie Macaulay * */ private class TDTooltip extends Tooltip { + private static final double TOOL_TIP_DELAY = 60; //seconds private TDPlotPane plotPanel; /** @@ -917,6 +918,8 @@ public class TDGraphFX extends PamBorderPane { public TDTooltip(TDPlotPane aPlot) { super("Test tooltip"); this.plotPanel = aPlot; + //make the Tooltip last a little longer + this.setShowDelay(Duration.seconds(TOOL_TIP_DELAY)); /* * See https://bugs.openjdk.java.net/browse/JDK-8090477 fr info about tool tip * timing on these displys. diff --git a/src/dataPlotsFX/overlaymark/menuOptions/RExportOverlayMenu.java b/src/dataPlotsFX/overlaymark/menuOptions/RExportOverlayMenu.java index fd6f6a0f..53083c39 100644 --- a/src/dataPlotsFX/overlaymark/menuOptions/RExportOverlayMenu.java +++ b/src/dataPlotsFX/overlaymark/menuOptions/RExportOverlayMenu.java @@ -44,7 +44,7 @@ public class RExportOverlayMenu extends ExportOverlayMenu { * Create the export overlay */ public RExportOverlayMenu(){ - rExportManger= new RExportManager(); + rExportManger= new RExportManager(null); buttonNode = createButton(); diff --git a/src/detectionPlotFX/plots/SpectrumPlot.java b/src/detectionPlotFX/plots/SpectrumPlot.java index 752130d7..a8a4971b 100644 --- a/src/detectionPlotFX/plots/SpectrumPlot.java +++ b/src/detectionPlotFX/plots/SpectrumPlot.java @@ -74,7 +74,7 @@ public abstract class SpectrumPlot implements Detection private SpectrumSettingsPane spectrumSettingsPane; - private double sR; + private double storedsR; // //TODO @@ -132,7 +132,7 @@ public abstract class SpectrumPlot implements Detection if (data ==null) return; - this.sR=sR; + this.storedsR=sR; int[] minmax = getAxisMinMaxSamples(plotProjector); @@ -172,7 +172,7 @@ public abstract class SpectrumPlot implements Detection } private double getSampleRate() { - return sR; + return storedsR; } @Override @@ -234,6 +234,8 @@ public abstract class SpectrumPlot implements Detection storedSpectrum=this.getPowerSpectrum(data, minmax[0], minmax[1]); storedCepstrum=this.getCepstrum(data, minmax[0], minmax[1]); + + storedsR = getSampleRate(data); if (spectrumPlotParams.logScale) { paintLogSpectrum(gc, clipRect,projector); diff --git a/src/export/CSVExport/CSVExportManager.java b/src/export/CSVExport/CSVExportManager.java index bf011e30..95e8520d 100644 --- a/src/export/CSVExport/CSVExportManager.java +++ b/src/export/CSVExport/CSVExportManager.java @@ -6,6 +6,10 @@ import java.util.List; import PamguardMVC.PamDataUnit; import export.PamDataUnitExporter; + +/** + * Export to CSV files which are RAVEN compatible. + */ public class CSVExportManager implements PamDataUnitExporter{ @Override @@ -41,4 +45,9 @@ public class CSVExportManager implements PamDataUnitExporter{ } + @Override + public boolean isNeedsNewFile() { + return false; + } + } diff --git a/src/export/MLExport/MLDetectionsManager.java b/src/export/MLExport/MLDetectionsManager.java index fe05cd39..eb6a1fbc 100644 --- a/src/export/MLExport/MLDetectionsManager.java +++ b/src/export/MLExport/MLDetectionsManager.java @@ -4,14 +4,11 @@ import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; import java.util.ArrayList; -import java.util.Comparator; import java.util.Date; import java.util.List; -import java.util.NoSuchElementException; import java.util.zip.Deflater; import PamUtils.PamArrayUtils; -import PamUtils.PamCalendar; import PamguardMVC.PamDataUnit; import export.PamDataUnitExporter; import us.hebi.matlab.mat.format.Mat5; @@ -21,7 +18,6 @@ import us.hebi.matlab.mat.types.Matrix; import us.hebi.matlab.mat.types.Sink; import us.hebi.matlab.mat.types.Sinks; import us.hebi.matlab.mat.types.Struct; -import us.hebi.matlab.mat.util.Casts; /** @@ -106,10 +102,11 @@ public class MLDetectionsManager implements PamDataUnitExporter { matFile.writeTo(sink); - matFile.close(); +// matFile.close(); //CAUSES AN EXCEPTION IF WRITING AGAIN } else { + //write to the mat file without loading all contents into memory. Mat5Writer writer = Mat5.newWriter(sink); @@ -257,7 +254,7 @@ public class MLDetectionsManager implements PamDataUnitExporter { @Override public void close() { - //handled in the mian funtion + //handled in the main function if (sink!=null) { try { sink.close(); @@ -269,6 +266,12 @@ public class MLDetectionsManager implements PamDataUnitExporter { } + @Override + public boolean isNeedsNewFile() { + // TODO Auto-generated method stub + return false; + } + diff --git a/src/export/MLExport/MLWhistleMoanExport.java b/src/export/MLExport/MLWhistleMoanExport.java index 124628ca..cdaba823 100644 --- a/src/export/MLExport/MLWhistleMoanExport.java +++ b/src/export/MLExport/MLWhistleMoanExport.java @@ -82,7 +82,7 @@ public class MLWhistleMoanExport extends MLDataUnitExport(); //add the MATLAB export pamExporters.add(new MLDetectionsManager()); - pamExporters.add(new RExportManager()); + pamExporters.add(new RExportManager(this)); pamExporters.add(new WavFileExportManager()); pamExporters.add(new CSVExportManager()); } @@ -72,6 +73,8 @@ public class PamExporterManager { public boolean exportDataUnit(PamDataUnit dataUnit, boolean force) { boolean exportOK = true; + System.out.println("Add data unit " + dataUnit + " to: "+ currentFile); + if (dataUnit==null) { if (force) { System.out.println("Write data 1!!" + dataUnitBuffer.size() ); @@ -83,10 +86,10 @@ public class PamExporterManager { } //if file is null or too large create another a file for saving. - if (currentFile == null || isFileSizeMax(currentFile)) { + if (currentFile == null || isNeedsNewFile(currentFile, pamExporters.get(exportParams.exportChoice))) { Date date = new Date(dataUnit.getTimeMilliseconds()); - String newFileName = "PAM_" + dataFormat.format(date); + String newFileName = "PAM_" + dataFormat.format(date) + "_" + dataUnit.getParentDataBlock().getDataName().replace(" ", "_"); //create a new file - note each exporter is responsible for closing the file after writing //so previous files should already be closed @@ -101,7 +104,7 @@ public class PamExporterManager { System.out.println("Write data unit " + dataUnitBuffer.size() + " to: "+ currentFile); if (dataUnitBuffer.size()>=BUFFER_SIZE || force) { - System.out.println("Write data 2!!" + dataUnitBuffer.size()); +// System.out.println("Write data 2!!" + dataUnitBuffer.size()); exportOK = pamExporters.get(exportParams.exportChoice).exportData(currentFile, dataUnitBuffer, true); dataUnitBuffer.clear(); } @@ -118,10 +121,14 @@ public class PamExporterManager { /** * Check whether the current file is greater than the maximum allowed file size. * @param currentFile2 - the current file + * @param pamDataUnitExporter * @return true of greater than or equal to the maximum file size. */ - private boolean isFileSizeMax(File currentFile2) { - return getFileSizeMegaBytes(currentFile2) >= MAX_FILE_SIZE_MB; + private boolean isNeedsNewFile(File currentFile2, PamDataUnitExporter pamDataUnitExporter) { + if( getFileSizeMegaBytes(currentFile2) >= exportParams.maximumFileSize) { + return true; + }; + return pamDataUnitExporter.isNeedsNewFile(); } /** diff --git a/src/export/RExport/RExportManager.java b/src/export/RExport/RExportManager.java index a6cf1add..826e96ae 100644 --- a/src/export/RExport/RExportManager.java +++ b/src/export/RExport/RExportManager.java @@ -18,6 +18,7 @@ import org.renjin.sexp.PairList.Builder; import PamUtils.PamArrayUtils; import PamguardMVC.PamDataUnit; import export.PamDataUnitExporter; +import export.PamExporterManager; import export.MLExport.MLDetectionsManager; /** @@ -29,7 +30,6 @@ import export.MLExport.MLDetectionsManager; public class RExportManager implements PamDataUnitExporter { /** - * * All the possible RDataUnit export classes. */ ArrayList rDataExport = new ArrayList(); @@ -37,10 +37,13 @@ public class RExportManager implements PamDataUnitExporter { private File currentFileName ; - private Builder allData; + private Builder allData; + + private PamExporterManager pamExporterManager; - public RExportManager(){ + public RExportManager(PamExporterManager pamExporterManager){ + this.pamExporterManager=pamExporterManager; /***Add more options here to export data units****/ rDataExport.add(new RClickExport()); rDataExport.add(new RWhistleExport()); @@ -55,17 +58,14 @@ public class RExportManager implements PamDataUnitExporter { * Note - there is no way to save data units to R files wothout loading the file into memory. * So everything is stored in memory until saved. */ - // then PamDataUnit minByTime = PamArrayUtils.getMinTimeMillis(dataUnits); - //matlab struct must start with a letter. + //MATLAB struct must start with a letter. Date date = new Date(minByTime.getTimeMilliseconds()); String entryName = "det_" + MLDetectionsManager.dataFormat.format( date); // System.out.println("Save R data! "+ dataUnits.size()); - // System.out.println("Export R file!!" + dataUnits.size()); - //is there an existing writer? Is that writer writing to the correct file? if (allData==null || !fileName.equals(currentFileName)) { @@ -259,5 +259,19 @@ public class RExportManager implements PamDataUnitExporter { } + @Override + public boolean isNeedsNewFile() { + //Rdata can't be appended to a file so we cannot check file sizes. + +// pamExporterManager.getExportParams().maximumFileSize; + + //TODO + //check file size against the export params. + System.out.println("RData length: " + allData.length()); + + return false; + } + + } diff --git a/src/export/swing/ExportProcessDialog.java b/src/export/swing/ExportProcessDialog.java index fd62ec6e..93184f13 100644 --- a/src/export/swing/ExportProcessDialog.java +++ b/src/export/swing/ExportProcessDialog.java @@ -9,7 +9,6 @@ import java.awt.Window; import java.awt.event.ActionListener; import java.io.File; import java.util.ArrayList; - import javax.swing.BoxLayout; import javax.swing.ButtonGroup; import javax.swing.JFileChooser; @@ -38,6 +37,8 @@ import export.PamExporterManager; import export.layoutFX.ExportParams; import offlineProcessing.OLProcessDialog; import offlineProcessing.OfflineTaskGroup; +import offlineProcessing.TaskMonitor; +import offlineProcessing.TaskMonitorData; import offlineProcessing.TaskStatus; /** @@ -75,7 +76,6 @@ public class ExportProcessDialog { public void createExportGroup() { - //clear current tasks. dlOfflineGroup.clearTasks(); @@ -87,7 +87,6 @@ public class ExportProcessDialog { dlOfflineGroup.addTask(new ExportTask(dataBlocks.get(i), exportManager)); } } - } ////---Swing stuff----/// should not be here but this is how PG works. @@ -129,6 +128,9 @@ public class ExportProcessDialog { */ private JFileChooser fc; + /** + * S hows the folder stuff is going to export to. + */ private JTextField exportTo; /** @@ -268,7 +270,7 @@ public class ExportProcessDialog { Ikon icon = null; /** - * This is nasty but we won't have many exporters and this is the only + * This is NASTY but we won't have many exporters and this is the only * good way to get this to work in Swing. */ switch (iconString) { @@ -351,20 +353,83 @@ public class ExportProcessDialog { } + + class ExportTaskMonitor implements TaskMonitor { + + private int taskIndex; + + private ExportTaskGroup exportTaskGroup; + + private boolean started = false; - - class ExportTaskGroup extends OfflineTaskGroup{ - - public ExportTaskGroup(String settingsName) { - super(null, settingsName); - // TODO Auto-generated constructor stub - + public ExportTaskMonitor(int i, ExportTaskGroup exportTaskGroup) { + this.taskIndex = i; + this.exportTaskGroup = exportTaskGroup; } + + @Override + public void setTaskStatus(TaskMonitorData taskMonitorData) { + if (taskMonitorData.taskStatus== TaskStatus.COMPLETE && !started) { + System.out.println(" TASK COMPLETE:"); + if (taskIndex>{ - + /** * Reference to the data exporter which manages exporting of data. */ private PamExporterManager exporter; - + /** * The data selector for the data block */ @@ -32,8 +33,8 @@ public class ExportTask extends OfflineTask>{ public ExportTask(PamDataBlock> parentDataBlock, PamExporterManager exporter) { super(parentDataBlock); this.exporter = exporter; - dataSelector=parentDataBlock.getDataSelectCreator().getDataSelector(this.getUnitName() +"_clicks", false, null); - + dataSelector=parentDataBlock.getDataSelectCreator().getDataSelector(this.getUnitName() +"_export", false, null); + } @@ -44,9 +45,20 @@ public class ExportTask extends OfflineTask>{ @Override public boolean processDataUnit(PamDataUnit dataUnit) { - if (dataSelector==null) exporter.exportDataUnit(dataUnit, false); - else if (dataSelector.scoreData(dataUnit)>0) { - exporter.exportDataUnit(dataUnit, false); + +// System.out.println("Huh? " + this.getDataBlock().getDataName() + " " + dataUnit + " " + dataUnit.getParentDataBlock().equals(this.getDataBlock())); + + if (dataUnit.getParentDataBlock().equals(this.getDataBlock())) { + //this is very important because the way the exporter works is that it iterates through multiple parent data blocks + System.out.println(this.getDataBlock().getDataName() + " " + dataUnit + " " + dataSelector.scoreData(dataUnit)); + if (dataSelector==null) { +// System.out.println("Data selector null: " + this.getDataBlock().getDataName() + " " + dataUnit); + exporter.exportDataUnit(dataUnit, false); + } + else if (dataSelector.scoreData(dataUnit)>=0 || dataSelector.getParams().getCombinationFlag() == DataSelectParams.DATA_SELECT_DISABLE) { +// System.out.println("Data selector OK: " + this.getDataBlock().getDataName() + " " + dataUnit); + exporter.exportDataUnit(dataUnit, false); + } } return false; //we don't need to indicate that anything has changed - we are just exporting. } @@ -54,18 +66,18 @@ public class ExportTask extends OfflineTask>{ @Override public void newDataLoad(long startTime, long endTime, OfflineDataMapPoint mapPoint) { // TODO Auto-generated method stub -// System.out.println("EXPORTER: new data load"); + // System.out.println("EXPORTER: new data load"); + } @Override public void loadedDataComplete() { System.out.println("EXPORTER: loaded data complete"); - //force the exporter so save any renaming data units in the buffer exporter.exportDataUnit(null, true); exporter.close(); exporter.setCurrentFile(null); - + } /** * task has settings which can be called @@ -80,13 +92,13 @@ public class ExportTask extends OfflineTask>{ * @return true if settings may have changed. */ public boolean callSettings() { - + dataSelector.getDialogPanel().setParams(); - + DataSelectDialog dataSelectDialog = new DataSelectDialog(PamController.getMainFrame(), this.getDataBlock(), dataSelector, null); return dataSelectDialog.showDialog(); - + } /** @@ -96,17 +108,17 @@ public class ExportTask extends OfflineTask>{ public boolean canExport(int exportSelection) { return exporter.getExporter(exportSelection).hasCompatibleUnits(getDataBlock().getUnitClass()); } - - + + @Override public boolean canRun() { boolean can = getDataBlock() != null; - + if (can) { //check whether we can export based on the export selection can = canExport(exporter.getExportParams().exportChoice); } - + return can; } diff --git a/src/export/wavExport/WavFileExportManager.java b/src/export/wavExport/WavFileExportManager.java index a765a0c7..67ffcc48 100644 --- a/src/export/wavExport/WavFileExportManager.java +++ b/src/export/wavExport/WavFileExportManager.java @@ -465,6 +465,13 @@ public class WavFileExportManager implements PamDataUnitExporter { + @Override + public boolean isNeedsNewFile() { + return false; + } + + + // hello(){ diff --git a/src/hfDaqCard/SmruDaqJNI.java b/src/hfDaqCard/SmruDaqJNI.java index e1b8356f..18c3b7e3 100644 --- a/src/hfDaqCard/SmruDaqJNI.java +++ b/src/hfDaqCard/SmruDaqJNI.java @@ -19,6 +19,9 @@ public class SmruDaqJNI { static public final int SMRU_VAL_SLAVE_MASTERED = 3; static public final int SMRU_RET_OK = 0; + + static public final int GREEN_LED = 1; + static public final int RED_LED = 0; /** * Have rebuilt SAIL Daq interface in 2022, but in principle this was @@ -29,11 +32,6 @@ public class SmruDaqJNI { private static final String SILIB = "SailDaqJNI"; // private static final String SILIB = "SailDaqV7"; - /** - * this is the verbose level for the C code part. - */ - private static final int verboseLevel = 0; - private static final String DEVNAME = "/dev/cypress_smru0"; private static final int MINJNIVERSION = 5; @@ -265,7 +263,7 @@ public class SmruDaqJNI { this.smruDaqSystem = smruDaqSystem; loadLibrary(); if (haveLibrary()) { - setVerbose(verboseLevel); + setVerbose(SmruDaqSystem.VERBOSELEVEL); /** * List the devices, but don't do any resetting and @@ -492,7 +490,7 @@ public class SmruDaqJNI { public int toggleLED(int board, int led) { board = boardOrder[board]; int state = getLED(board, led); - System.out.println("state="+state); +// System.out.println("LED state="+state); if (state == 0) { state = 1; } diff --git a/src/hfDaqCard/SmruDaqSystem.java b/src/hfDaqCard/SmruDaqSystem.java index c6af0d72..b9961924 100644 --- a/src/hfDaqCard/SmruDaqSystem.java +++ b/src/hfDaqCard/SmruDaqSystem.java @@ -5,6 +5,7 @@ import java.util.List; import javax.swing.JComponent; import javax.swing.JOptionPane; +import javax.swing.SwingUtilities; import org.w3c.dom.Document; import org.w3c.dom.Element; @@ -13,6 +14,7 @@ import soundPlayback.PlaybackControl; import soundPlayback.PlaybackSystem; import Acquisition.AcquisitionControl; import Acquisition.AcquisitionDialog; +import Acquisition.AcquisitionProcess; import Acquisition.DaqSystem; import Acquisition.AudioDataQueue; import PamController.PamControlledUnitSettings; @@ -49,7 +51,8 @@ public class SmruDaqSystem extends DaqSystem implements PamSettings { public static final String oldCardName = "SMRU Ltd DAQ Card"; - private static final int VERBOSELEVEL = 0; + public static final int VERBOSELEVEL = 0; + /** * @param daqControl @@ -61,6 +64,11 @@ public class SmruDaqSystem extends DaqSystem implements PamSettings { smruDaqJNI = new SmruDaqJNI(this); nDaqCards = smruDaqJNI.getnDevices(); + for (int i = 0; i < nDaqCards; i++) { + for (int l = 0; l < 2; l++) { + smruDaqJNI.setLED(i, l, 0); + } + } smruDaqDialogPanel = new SmruDaqDialogPanel(this); PamSettingManager.getInstance().registerSettings(this); @@ -275,6 +283,8 @@ public class SmruDaqSystem extends DaqSystem implements PamSettings { boolean prepareDaqCard(int iBoard, boolean fullReset) { // devices are left closed, so will need to reopen them. // of course, there is a vile lookup table, so... + smruDaqJNI.setLED(iBoard, SmruDaqJNI.GREEN_LED, 0); + smruDaqJNI.setLED(iBoard, SmruDaqJNI.RED_LED, 0); int hardId = smruDaqJNI.getBoardOrder(iBoard); int prepOk = smruDaqJNI.prepareDevice(hardId, fullReset); terminalPrint("Opened daq card returned " + prepOk, 1); @@ -313,6 +323,7 @@ public class SmruDaqSystem extends DaqSystem implements PamSettings { if (ans) { Thread t = new Thread(new DaqThread()); + t.setPriority(Thread.MAX_PRIORITY); t.start(); keepRunning = true; daqThreadRunning = true; @@ -371,18 +382,43 @@ public class SmruDaqSystem extends DaqSystem implements PamSettings { boolean first = true; boolean needRestart = false; int iChan; + // some flags on checks of incoming data rate +// long recentSamples = 0; +// long recentCheckTime = System.currentTimeMillis(); + int loopCount = 0; + for (int iBoard = 0; iBoard < nDaqCards; iBoard++) { + smruDaqJNI.setLED(iBoard, SmruDaqJNI.GREEN_LED, 1); + smruDaqJNI.setLED(iBoard, SmruDaqJNI.RED_LED, 0); + } + while (keepRunning) { iChan = 0; dataMillis = daqControl.getAcquisitionProcess().absSamplesToMilliseconds(totalSamples); + if (isStalled()) { + needRestart = true; + for (int iBoard = 0; iBoard < nDaqCards; iBoard++) { + smruDaqJNI.setLED(iBoard, SmruDaqJNI.GREEN_LED, 0); + smruDaqJNI.setLED(iBoard, SmruDaqJNI.RED_LED, 1); + } + // don't set this false or the shutdown doesn't work properly. +// keepRunning = false; + break; + } + loopCount++; for (int iBoard = 0; iBoard < nDaqCards && keepRunning; iBoard++) { + if ((loopCount % 10000) == 0) { +// toggleLED(iBoard, 0); + smruDaqJNI.setLED(iBoard, SmruDaqJNI.GREEN_LED, 1); + } for (int i = 0; i < boardChannels[iBoard] && keepRunning; i++) { newData = smruDaqJNI.readSamples(iBoard, i, wantedSamples); if (newData == null) { + smruDaqJNI.setLED(iBoard, SmruDaqJNI.RED_LED, 1); System.out.println(String.format("Null data read from smruDaqJNI.readSamples board %d, chan %d, samples %d", iBoard, i, wantedSamples)); // System.out.println("Issue restart ..."); needRestart = true; - keepRunning = false; +// keepRunning = false; break; } readSamples = newData.length; @@ -409,19 +445,47 @@ public class SmruDaqSystem extends DaqSystem implements PamSettings { if (++dcOffsetCalls == 100) { dcOffsetScale = 100.; // after a bit, increase the time constant. } + totalSamples += readSamples; - } + }; daqThreadRunning = false; - if (needRestart) { - PamController pamController = PamController.getInstance(); - pamController.pamStop(); - PamDialog.showWarning(daqControl.getGuiFrame(), daqControl.getUnitName(), "Problem with one or more SAIL DAQ Cards.\n" - + "Restart PAMGuard, check connections and try again." ); -// pamController.startLater(); + + for (int iBoard = 0; iBoard < nDaqCards; iBoard++) { + smruDaqJNI.setLED(iBoard, SmruDaqJNI.GREEN_LED, 0); } + + if (needRestart) { + SwingUtilities.invokeLater(new Runnable() { + + @Override + public void run() { + PamController pamController = PamController.getInstance(); + pamController.pamStop(); + System.out.println("Problem with one or more SAIL DAQ Cards. " + + "Restart PAMGuard, check connections and try again." ); +// PamDialog.showWarning(daqControl.getGuiFrame(), daqControl.getUnitName(), "Problem with one or more SAIL DAQ Cards.\n" +// + "Restart PAMGuard, check connections and try again." ); + pamController.startLater(); + } + }); + } + } + +// private long lastFakeStall = 0; + private boolean isStalled() { +// if (lastFakeStall == 0) { +// lastFakeStall = System.currentTimeMillis(); +// } +// long now = System.currentTimeMillis(); +// if (now-lastFakeStall > 10000) { +// System.out.println("Random pretend stalled"); +// lastFakeStall = 0; +// return true; +// } + return daqControl.getAcquisitionProcess().isStalled(); } public static short getSample(byte[] buffer, int position) { diff --git a/src/noiseBandMonitor/NoiseBandControl.java b/src/noiseBandMonitor/NoiseBandControl.java index c9b0242d..7f85fa96 100644 --- a/src/noiseBandMonitor/NoiseBandControl.java +++ b/src/noiseBandMonitor/NoiseBandControl.java @@ -236,9 +236,9 @@ public class NoiseBandControl extends PamControlledUnit implements PamSettings { @Override public boolean restoreSettings(PamControlledUnitSettings pamControlledUnitSettings) { noiseBandSettings = ((NoiseBandSettings)pamControlledUnitSettings.getSettings()).clone(); - System.out.println("********************************************************"); - System.out.println("NOISE BAND SETTINGS: " + noiseBandSettings.rawDataSource); - System.out.println("********************************************************"); +// System.out.println("********************************************************"); +// System.out.println("NOISE BAND SETTINGS: " + noiseBandSettings.rawDataSource); +// System.out.println("********************************************************"); return true; } diff --git a/src/offlineProcessing/OfflineTaskGroup.java b/src/offlineProcessing/OfflineTaskGroup.java index 40c37c66..e3c8f4ca 100644 --- a/src/offlineProcessing/OfflineTaskGroup.java +++ b/src/offlineProcessing/OfflineTaskGroup.java @@ -142,7 +142,7 @@ public class OfflineTaskGroup implements PamSettings { private ArrayList offlineTasks = new ArrayList(); - private TaskGroupWorker worker; + protected TaskGroupWorker worker; private OfflineSuperDetFilter superDetectionFilter; @@ -377,7 +377,7 @@ public class OfflineTaskGroup implements PamSettings { * @author Doug Gillespie * */ - class TaskGroupWorker extends SwingWorker implements ViewLoadObserver { + public class TaskGroupWorker extends SwingWorker implements ViewLoadObserver { volatile boolean instantKill = false; @@ -437,6 +437,7 @@ public class OfflineTaskGroup implements PamSettings { /** * Process all data for a list of time chunks. This is robust to the list * not being in chronological order. + * * @param timeChunks - the time chunks. */ private void processAllData(ArrayList timeChunks){ @@ -976,4 +977,6 @@ public class OfflineTaskGroup implements PamSettings { offlineTasks.clear(); } + + } diff --git a/src/pamViewFX/PamGuiFX.java b/src/pamViewFX/PamGuiFX.java index a9d4c678..f19b12d5 100644 --- a/src/pamViewFX/PamGuiFX.java +++ b/src/pamViewFX/PamGuiFX.java @@ -35,6 +35,7 @@ import dataModelFX.DataModelPaneFX; import org.controlsfx.control.ToggleSwitch; + /** * A pane which holds a set of tabs. * @@ -125,7 +126,7 @@ public class PamGuiFX extends StackPane implements PamViewInterface { this.pamGuiManagerFX=pamGuiManagerFX; this.mainTabPane = mainTabPane; - Pane layout=createMainPane(mainTabPane, stage); + Node layout=createMainPane(mainTabPane, stage); //add main pane to PamGui this.getChildren().add(layout); @@ -140,7 +141,7 @@ public class PamGuiFX extends StackPane implements PamViewInterface { //create the main tab pane. this.mainTabPane = new PamTabPane(); - Pane layout=createMainPane(mainTabPane, stage); + Node layout=createMainPane(mainTabPane, stage); //add main pane to PamGui this.getChildren().add(layout); @@ -154,7 +155,7 @@ public class PamGuiFX extends StackPane implements PamViewInterface { * @param stage - the stage holding this GUI. * @return a pane which sits in the stage. */ - private Pane createMainPane(PamTabPane mainTabPane, Stage stage){ + private Node createMainPane(PamTabPane mainTabPane, Stage stage){ //create the pane which holds tab pane final PamBorderPane layout = new PamBorderPane(); @@ -261,7 +262,6 @@ public class PamGuiFX extends StackPane implements PamViewInterface { PamBorderPane layoutHolder=new PamBorderPane(layout); layoutHolder.setTop(hidingLoadPane); - return layoutHolder; diff --git a/src/pamViewFX/PamGuiTabFX.java b/src/pamViewFX/PamGuiTabFX.java index 9e1f3fb2..42fc18d8 100644 --- a/src/pamViewFX/PamGuiTabFX.java +++ b/src/pamViewFX/PamGuiTabFX.java @@ -253,6 +253,10 @@ public class PamGuiTabFX extends PamTabFX { double r1 = 1 - r; int smallWindows = 0; + + + //the padding between windows + double padding = 10; ArrayList dw = internalPanes; @@ -268,7 +272,9 @@ public class PamGuiTabFX extends PamTabFX { //now place windows in correct position //large windows - double x, y, w, h = 0; + double x, y, w, h = 0.; + double pad =0.; + if (largeWindows > 0) { x = 0; y = 0; @@ -284,7 +290,10 @@ public class PamGuiTabFX extends PamTabFX { if (dw.get(i).getUserDisplayNode().isMinorDisplay()== true) continue; dw.get(i).setPaneLayout(x, y); - dw.get(i).setPaneSize(w, h); + + //set the padding if the pane is not the last pane. + pad = (i== dw.size()-1) ? 0 : padding; + dw.get(i).setPaneSize(w - (horz? pad:0), h - (horz? 0:pad)); if (horz) x += w; else y += h; } @@ -307,7 +316,10 @@ public class PamGuiTabFX extends PamTabFX { for (int i = 0; i < dw.size(); i++) { if (dw.get(i).getUserDisplayNode().isMinorDisplay() == false) continue; dw.get(i).setPaneLayout(x, y); - dw.get(i).setPaneSize(w, h); + + //set the padding if the pane is not the last pane. + pad = (i== dw.size()-1) ? 0 : padding; + dw.get(i).setPaneSize(w- (horz? pad:0), h - (horz? 0:pad)); if (horz) x += w; else y +=h; } diff --git a/src/pamViewFX/fxNodes/flipPane/PamFlipPane.java b/src/pamViewFX/fxNodes/flipPane/PamFlipPane.java index 756748bd..5a88eb5f 100644 --- a/src/pamViewFX/fxNodes/flipPane/PamFlipPane.java +++ b/src/pamViewFX/fxNodes/flipPane/PamFlipPane.java @@ -128,7 +128,7 @@ public class PamFlipPane extends FlipPane { * Convenience duplicate of getBackPane(). * @return the back pane. */ - public PamBorderPane getAdvPane() { + public PamBorderPane getAdvContentPane() { return advPane; } diff --git a/src/pamViewFX/fxNodes/hidingPane/HidingPane.java b/src/pamViewFX/fxNodes/hidingPane/HidingPane.java index 9e2ef7d8..2ad19062 100644 --- a/src/pamViewFX/fxNodes/hidingPane/HidingPane.java +++ b/src/pamViewFX/fxNodes/hidingPane/HidingPane.java @@ -20,7 +20,6 @@ import javafx.beans.property.DoubleProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.event.ActionEvent; import javafx.event.EventHandler; -import javafx.geometry.Insets; /** * Hiding pane which can be added to any node. @@ -156,7 +155,7 @@ public class HidingPane extends StackPane { //create a button which hides the side panel hideButton=createShowButton(false); styleHideButton(hideButton); - hideButton.setOnAction(new HideButtonPressed()); +// hideButton.setOnAction(new HideButtonPressed()); hideButton.setVisible(false); // hideButton.setStyle("close-button-right"); @@ -333,8 +332,6 @@ public class HidingPane extends StackPane { double dragX=0; double dragY=0; double distance=0; - - private PamButton createShowButton(final boolean show){ final PamButton pamButton=new PamButton(); @@ -343,6 +340,7 @@ public class HidingPane extends StackPane { @Override public void handle(MouseEvent mouseEvent) { // record a delta distance for the drag and drop operation. +// System.out.println("HidingPane.showButton - setOnMousePressed"); dragX =mouseEvent.getSceneX(); dragY =mouseEvent.getSceneY(); } @@ -350,10 +348,24 @@ public class HidingPane extends StackPane { pamButton.setOnMouseReleased(new EventHandler() { @Override public void handle(MouseEvent mouseEvent) { - if (distance==0) return; +// System.out.println("HidingPane.showButton - setOnMouseReleased"); + + //the mouse has been pressed + if (distance==0) { + if (isShowing()) { + showHidePane(false); + } + else { + showHidePane(true); + } + return; + } + + //the mouse has been dragged dragged distance=sideIndex*distance; // System.out.println("Mouse released: HidePanel distance: "+distance); if (!overlay) distance=Math.abs(distance); + //need to see where the drag has ended-if greater than 50% then open but if less then close. if (!isHorizontal()){ if (distance=expandedSize/2) showHidePane(overlay? false : true); } + //reset the distance distance=0; } @@ -370,12 +383,14 @@ public class HidingPane extends StackPane { pamButton.setOnMouseDragged(new EventHandler() { @Override public void handle(MouseEvent mouseEvent) { +// System.out.println("HidingPane.showButton - setOnMouseDragged"); if (visibleImmediatly) hidePane.setVisible(true); else hidePane.setVisible(false); // hideButton.setVisible(true); /** * Work out the distance the panel is to be dragged; */ + double distance = 0; if (!isHorizontal()){ if (!show) distance=(mouseEvent.getSceneX()-dragX); else distance=(mouseEvent.getSceneX()-dragX)+sideIndex*expandedSize; @@ -387,26 +402,28 @@ public class HidingPane extends StackPane { if (!overlay) distance=expandedSize+sideIndex*distance; } // if (show && Math.abs(distance)>expandedSize) return; - + translatePanel(distance); + + HidingPane.this.distance = distance ; } }); - pamButton.setOnMouseEntered(new EventHandler() { - @Override public void handle(MouseEvent mouseEvent) { -// System.out.println("HidingPane.showButton - mouse entered"); - pamButton.setOpacity(1.0); - pamButton.setPadding(new Insets(2.,2.,2.,2.)); - } - }); - - pamButton.setOnMouseExited(new EventHandler() { - @Override public void handle(MouseEvent mouseEvent) { -// System.out.println("HidingPane.showButton - mouse exited"); - if (show) pamButton.setOpacity(showButtonOpacity); - pamButton.setPadding(new Insets(0.,0.,0.,0.)); - } - }); +// pamButton.setOnMouseEntered(new EventHandler() { +// @Override public void handle(MouseEvent mouseEvent) { +//// System.out.println("HidingPane.showButton - mouse entered"); +// pamButton.setOpacity(1.0); +// pamButton.setPadding(new Insets(2.,2.,2.,2.)); +// } +// }); +// +// pamButton.setOnMouseExited(new EventHandler() { +// @Override public void handle(MouseEvent mouseEvent) { +//// System.out.println("HidingPane.showButton - mouse exited"); +// if (show) pamButton.setOpacity(showButtonOpacity); +// pamButton.setPadding(new Insets(0.,0.,0.,0.)); +// } +// }); // pamButton.setOnMouseClicked(new EventHandler() { // @Override public void handle(MouseEvent mouseEvent) { @@ -415,15 +432,15 @@ public class HidingPane extends StackPane { // } // }); - pamButton.addEventHandler(ActionEvent.ACTION,new EventHandler() { - @Override - public void handle(ActionEvent e) { - System.out.println("HidingPane.showButton - action event clicked"); - showHidePane(show); - } - }); +// pamButton.addEventHandler(ActionEvent.ACTION,new EventHandler() { +// @Override +// public void handle(ActionEvent e) { +// System.out.println("HidingPane.showButton - action event clicked"); +// showHidePane(show); +// } +// }); - if (show) pamButton.setOpacity(showButtonOpacity); +// if (show) pamButton.setOpacity(showButtonOpacity); return pamButton; } @@ -601,7 +618,7 @@ public class HidingPane extends StackPane { showing.setValue(true); if (visibleImmediatly) hidePane.setVisible(true); //hideButton.setVisible(true); - //System.out.println("HidingPane: Open Hide Pane"); +// System.out.println("HidingPane: Open Hide Pane"); //open the panel if (timeLineShow.getStatus()==Status.RUNNING) { //stops the issue with the hiding pane freezing. @@ -613,7 +630,7 @@ public class HidingPane extends StackPane { else{ showing.setValue(false); if (!visibleImmediatly) hidePane.setVisible(false); - //System.out.println("HidingPane: Close Hide Pane"); +// System.out.println("HidingPane: Close Hide Pane"); //close the panel timeLineHide.play(); } @@ -638,17 +655,17 @@ public class HidingPane extends StackPane { } } - /** - * Called whenever the pin button is pressed. - * @author Jamie Macaulay - */ - class HideButtonPressed implements EventHandler{ - - @Override - public void handle(ActionEvent arg0) { - showHidePane(false); - } - } +// /** +// * Called whenever the pin button is pressed. +// * @author Jamie Macaulay +// */ +// class HideButtonPressed implements EventHandler{ +// +// @Override +// public void handle(ActionEvent arg0) { +// showHidePane(false); +// } +// } /** * Get the button which hides the pane. @@ -732,6 +749,15 @@ public class HidingPane extends StackPane { return this.showing; } + /** + * Check whether the hiding pane is showing. + * @return true if showing. + */ + public boolean isShowing(){ + return this.showing.get(); + } + + /** * Get the opacity of the show button when the mouse is outside the button. * @return the opacity of the show button. diff --git a/src/pamViewFX/fxNodes/utilsFX/ColourArray.java b/src/pamViewFX/fxNodes/utilsFX/ColourArray.java index 536fa9d3..3d60628f 100644 --- a/src/pamViewFX/fxNodes/utilsFX/ColourArray.java +++ b/src/pamViewFX/fxNodes/utilsFX/ColourArray.java @@ -244,7 +244,6 @@ public class ColourArray implements Cloneable, Serializable{ colors[4]=Color.web("#0000ff"); colors[5]=Color.web("#ff00ff"); colors[6]=Color.web("#ff0000"); - return colors; case PATRIOTIC: colors=new Color[3]; @@ -254,22 +253,22 @@ public class ColourArray implements Cloneable, Serializable{ return colors; case MATLAB: colors=new Color[7]; - colors[0]= Color.color( 0, 0.4470, 0.7410); - colors[1]= Color.color(0.8500 , 0.3250 , 0.0980); - colors[2]= Color.color(0.9290 , 0.6940 , 0.1250); - colors[3]= Color.color( 0.4940 , 0.1840 , 0.5560); - colors[4]= Color.color(0.4660 , 0.6740 , 0.1880); - colors[5]= Color.color( 0.3010 , 0.7450 , 0.9330); - colors[6]= Color.color( 0.6350 , 0.0780 , 0.1840); + colors[0]= Color.color(0, 0.4470, 0.7410); + colors[1]= Color.color(0.8500, 0.3250, 0.0980); + colors[2]= Color.color(0.9290, 0.6940, 0.1250); + colors[3]= Color.color(0.4940, 0.1840, 0.5560); + colors[4]= Color.color(0.4660, 0.6740, 0.1880); + colors[5]= Color.color(0.3010, 0.7450, 0.9330); + colors[6]= Color.color(0.6350, 0.0780, 0.1840); return colors; case PARULA: colors=new Color[6]; - colors[0]= Color.color( 0.24220, 0.150400, 0.66030); - colors[1]= Color.color( 0.264700 ,0.403000 , 0.993500); - colors[2]= Color.color( 0.108500, 0.66690 , 0.873400); - colors[3]= Color.color( 0.280900 , 0.796400, 0.526600); - colors[4]= Color.color( 0.918400, 0.73080 , 0.18900); - colors[5]= Color.color( 0.9769 , 0.98390 ,0.0805000); + colors[0]= Color.color(0.24220, 0.150400, 0.66030); + colors[1]= Color.color(0.264700, 0.403000, 0.993500); + colors[2]= Color.color(0.108500, 0.66690, 0.873400); + colors[3]= Color.color(0.280900, 0.796400, 0.526600); + colors[4]= Color.color(0.918400, 0.73080, 0.18900); + colors[5]= Color.color(0.9769, 0.98390, 0.0805000); return colors; case INFERNO: return InfernoColorMap.getInfernoColourMap(); diff --git a/src/pamViewFX/fxNodes/utilsFX/PamUtilsFX.java b/src/pamViewFX/fxNodes/utilsFX/PamUtilsFX.java index a0d976b2..39d610ea 100644 --- a/src/pamViewFX/fxNodes/utilsFX/PamUtilsFX.java +++ b/src/pamViewFX/fxNodes/utilsFX/PamUtilsFX.java @@ -16,9 +16,9 @@ import javax.swing.JMenuItem; import javax.swing.JPopupMenu; import javax.swing.JPopupMenu.Separator; import javax.swing.JRadioButtonMenuItem; -import javax.swing.JSeparator; import javax.swing.SwingUtilities; +import PamView.ColourArray.ColourArrayType; import PamView.PamSymbol; import javafx.animation.KeyFrame; import javafx.animation.KeyValue; @@ -54,6 +54,26 @@ import pamViewFX.fxNodes.PamSymbolFX; */ public class PamUtilsFX { + /** + * Convert an FX based ColourArrayType to Swing. Note that swing options that do not exist in FX and vice versa will return null. + * @param arrayFX - the FX ColourArrayType. + * @return the Swing ColourArrayType; + */ + public static ColourArrayType fxColArray2Swing(pamViewFX.fxNodes.utilsFX.ColourArray.ColourArrayType arrayFX) { + ColourArrayType type = ColourArrayType.valueOf(arrayFX.toString()); + return type; + } + + /** + * Convert an Swing based ColourArrayType to FX. Note that swing options that do not exist in FX and vice versa will return null. + * @param arraySwing - the Swing ColourArrayType. + * @return the FX ColourArrayType. + */ + public static pamViewFX.fxNodes.utilsFX.ColourArray.ColourArrayType swingColArray2FX(ColourArrayType arraySwing) { + pamViewFX.fxNodes.utilsFX.ColourArray.ColourArrayType type = pamViewFX.fxNodes.utilsFX.ColourArray.ColourArrayType.valueOf(arraySwing.toString()); + return type; + } + /** * * @param awtColor @@ -632,13 +652,36 @@ public class PamUtilsFX { * @param color - the color. * @return the color. */ - public static String toRGBCode( Color color ) - { - return String.format( "#%02X%02X%02X", - (int)( color.getRed() * 255 ), - (int)( color.getGreen() * 255 ), - (int)( color.getBlue() * 255 ) ); - } - + public static String toRGBCode( Color color ){ + return String.format( "#%02X%02X%02X", + (int)( color.getRed() * 255 ), + (int)( color.getGreen() * 255 ), + (int)( color.getBlue() * 255 ) ); + } + + + /** + * Convert a colour to an int. + * @param c - the colour to change. + * @return the int representation of the colour + */ + public static int colorToInt(Color c) { + int r = (int) Math.round(c.getRed() * 255); + int g = (int) Math.round(c.getGreen() * 255); + int b = (int) Math.round(c.getBlue() * 255); + return (r << 16) | (g << 8) | b; + } + + /** + * Convert an int encoded with a colour to a Color object. + * @param value - the int to convert to colour + * @return the Color object for the int + */ + public static Color intToColor(int value) { + int r = (value >>> 16) & 0xFF; + int g = (value >>> 8) & 0xFF; + int b = value & 0xFF; + return Color.rgb(r,g,b); + } } diff --git a/src/pamViewFX/fxPlotPanes/PlotPane.java b/src/pamViewFX/fxPlotPanes/PlotPane.java index f85fc79b..30355c8e 100644 --- a/src/pamViewFX/fxPlotPanes/PlotPane.java +++ b/src/pamViewFX/fxPlotPanes/PlotPane.java @@ -1,6 +1,9 @@ package pamViewFX.fxPlotPanes; import Layout.PamAxis; +import javafx.beans.binding.Bindings; +import javafx.beans.property.SimpleDoubleProperty; +import javafx.beans.value.ObservableValue; import javafx.geometry.Side; import javafx.scene.Node; import javafx.scene.canvas.Canvas; @@ -256,13 +259,31 @@ public class PlotPane extends PamBorderPane { PamHBox horzHolder=new PamHBox(); Pane leftPane=new Pane(); + + //create an observable which is the size of the axis pane if the pane is visible and otherwise + //is zero. + ObservableValue valLeft = Bindings + .when(yAxisLeftPane.visibleProperty()) + .then(yAxisLeftPane.widthProperty()) + .otherwise( + new SimpleDoubleProperty(0.) + ); + //need both min and pref to make binding work properly; - leftPane.prefWidthProperty().bind(yAxisLeftPane.widthProperty()); - leftPane.minWidthProperty().bind(yAxisLeftPane.widthProperty()); + leftPane.prefWidthProperty().bind(valLeft); + leftPane.minWidthProperty().bind(valLeft); Pane rightPane=new Pane(); - rightPane.prefWidthProperty().bind(yAxisRightPane.widthProperty()); - rightPane.minWidthProperty().bind(yAxisRightPane.widthProperty()); + + ObservableValue valRight = Bindings + .when(yAxisRightPane.visibleProperty()) + .then(yAxisRightPane.widthProperty()) + .otherwise( + new SimpleDoubleProperty(0.) + ); + + rightPane.prefWidthProperty().bind(valRight); + rightPane.minWidthProperty().bind(valRight); horzHolder.getChildren().addAll(leftPane, axisPane, rightPane); //axisPane.toFront(); this changes the order of children in a PamHBox. @@ -369,8 +390,8 @@ public class PlotPane extends PamBorderPane { //holderPane.getChildren().clear(); - //HACK- 05/08/2016 have to do this because there is a bug in switching children postions in a border pane. - //casues a duplicate childrne error. + //HACK- 05/08/2016 have to do this because there is a bug in switching children positions in a border pane. + //causes duplicate children error holderPane.setRight(null); holderPane.setLeft(null); holderPane.setTop(null); @@ -385,24 +406,30 @@ public class PlotPane extends PamBorderPane { else if (topBorder > 0) { // holderPane.setTopSpace(topBorder); } + if (bottom) { holderPane.setBottom(bottomHolder); } else if (bottomBorder > 0) { // holderPane.setBottomSpace(bottomBorder); } + if (right) { holderPane.setRight(yAxisRightPane) ; + yAxisRightPane.setVisible(true); } - else if (rightBorder > 0){ - // holderPane.setRightSpace(rightBorder); + else { + yAxisRightPane.setVisible(false); } + if (left) { holderPane.setLeft(yAxisLeftPane) ; + yAxisLeftPane.setVisible(true); } - else if (leftBorder > 0) { - // holderPane.setLeftSpace(leftBorder); + else { + yAxisLeftPane.setVisible(false); } + holderPane.setCenter(canvasHolder); //bottomHolder.toBack(); diff --git a/src/pamViewFX/symbol/PeakFreqOptionsPane.java b/src/pamViewFX/symbol/PeakFreqOptionsPane.java index f292c460..fb59c6b4 100644 --- a/src/pamViewFX/symbol/PeakFreqOptionsPane.java +++ b/src/pamViewFX/symbol/PeakFreqOptionsPane.java @@ -179,6 +179,17 @@ public class PeakFreqOptionsPane extends StandardSymbolModifierPane { DoubleSpinnerValueFactory spinnerValFact = (DoubleSpinnerValueFactory) maxFreq.getValueFactory(); spinnerValFact.maxProperty().set(getSampleRate() /2); + + //set reasonable step sizes + if (getSampleRate()>=10000) { + spinnerValFact.amountToStepByProperty().set(1000.); + } + else if (getSampleRate()>=2000){ + spinnerValFact.amountToStepByProperty().set(200.); + } + else { + spinnerValFact.amountToStepByProperty().set(50.); + } if (symbolOptions.freqLimts==null) { diff --git a/src/rawDeepLearningClassifier/DLControl.java b/src/rawDeepLearningClassifier/DLControl.java index 4b7d3713..a90cfa7f 100644 --- a/src/rawDeepLearningClassifier/DLControl.java +++ b/src/rawDeepLearningClassifier/DLControl.java @@ -561,6 +561,7 @@ public class DLControl extends PamControlledUnit implements PamSettings { * @return the number of classes. */ public int getNumClasses() { + if (getDLModel()==null) return 0; return getDLModel().getNumClasses(); } @@ -622,8 +623,9 @@ public class DLControl extends PamControlledUnit implements PamSettings { } /** - * Get the classifier chooser. - * @return the classifier chooser.Take it + * Get the classifier chooser. The classifier chooser chooses which classifier use + * based on a selected file or URL. + * @return the classifier chooser. */ public DLClassifierChooser getDlClassifierChooser() { return dlClassifierChooser; diff --git a/src/rawDeepLearningClassifier/dataPlotFX/DLPredictionPane.java b/src/rawDeepLearningClassifier/dataPlotFX/DLPredictionPane.java index 19147c81..2d05d4af 100644 --- a/src/rawDeepLearningClassifier/dataPlotFX/DLPredictionPane.java +++ b/src/rawDeepLearningClassifier/dataPlotFX/DLPredictionPane.java @@ -19,7 +19,9 @@ import pamViewFX.fxNodes.utilityPanes.PamToggleSwitch; import rawDeepLearningClassifier.dlClassification.DLClassName; /* - * Symbol Options for the annotation pane + * Symbol Options for the annotation pane. + * + * @author Jamie Macaulay */ public class DLPredictionPane extends PamBorderPane implements TDSettingsPane { diff --git a/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolModifier.java b/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolModifier.java index ce94692a..4b2a32e3 100644 --- a/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolModifier.java +++ b/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolModifier.java @@ -1,9 +1,14 @@ package rawDeepLearningClassifier.dataPlotFX; import java.awt.Color; +import java.awt.event.ActionEvent; +import PamController.PamController; +import PamUtils.PamArrayUtils; import PamView.GeneralProjector; import PamView.PamSymbolType; +import PamView.dialog.GenericSwingDialog; +import PamView.dialog.PamDialogPanel; import PamView.symbol.PamSymbolChooser; import PamView.symbol.SymbolData; import PamView.symbol.modifier.SymbolModType; @@ -20,9 +25,10 @@ import rawDeepLearningClassifier.logging.DLAnnotationType; /** - * The DL symbol modifier. Colours symbols by prediction. + * The DL symbol modifier. Colours symbols by either the value of the prediction + * by a user selected class or by the class with the highest prediction value. * - * @author Jamie Macaulay. + * @author Jamie Macaulay. * */ public class DLSymbolModifier extends SymbolModifier { @@ -36,12 +42,12 @@ public class DLSymbolModifier extends SymbolModifier { /** - * The symbol options pane. + * JavaFX symbol options pane. */ private DLSymbolOptionPane optionsPane; /** - * Rge DL annotation type. + * The DL annotation type. */ private DLAnnotationType dlAnnotType; @@ -55,6 +61,11 @@ public class DLSymbolModifier extends SymbolModifier { private ColourArray colourArray; + /** + * Swing option panel for the symbol chooser. + */ + private DLSymbolOptionPanel optionsPanel; + public DLSymbolModifier(PamSymbolChooser symbolChooser, DLAnnotationType dlAnnotType) { @@ -94,7 +105,70 @@ public class DLSymbolModifier extends SymbolModifier { return null; } - //System.out.println("Class index: " + dlSymbolOptions.classIndex); + + //modify the default symbol + if (dlSymbolOptions.colTypeSelection == DLSymbolModifierParams.PREDICITON_COL) { + getSymbolDataPred(annotation); + } + + else if (dlSymbolOptions.colTypeSelection == DLSymbolModifierParams.CLASS_COL) { + getSymbolDataClass(annotation); + } + + return symbolData; + } + + /** + * Get symbol data for colouring by the species class with the maximum prediction + * @param annotation - the annotation + * @return symbol data for colouring by class maximum. + */ + private SymbolData getSymbolDataClass(DLAnnotation annotation ) { + + boolean passed = false; + int colIndex = -1; + + float[][] results = new float[ annotation.getModelResults().size()][]; + + //A detection might have multiple prediction results, i.e. predictions are a matrix. Need + //to iterate through all the predictions and then work out whihc is the maximum. That index is then then] + //class colour. + int i=0; + for (PredictionResult modelResult: annotation.getModelResults()) { + if (modelResult.isBinaryClassification()) passed = true; + results[i] = modelResult.getPrediction(); + i++; + } + + int[] indexBest = PamArrayUtils.maxPos(results); + + + if (passed || !dlSymbolOptions.showOnlyBinary) { + //work out the class colour... + + javafx.scene.paint.Color color = PamUtilsFX.intToColor(dlSymbolOptions.classColors[indexBest[1]]); + + Color colorAWT = PamUtilsFX.fxToAWTColor(color); + + symbolData.setFillColor(colorAWT); + symbolData.setLineColor(colorAWT); + + return symbolData; + } + else { + //has data but we have only show binary option selected. + return null; + } + + } + + + /** + * Get symbol data for colouring by the prediction value for a selected species class. + * @param annotation - the annotation + * @return symbol data for colouring by prediction value for a selected species class. + */ + private SymbolData getSymbolDataPred(DLAnnotation annotation ) { if (dlSymbolOptions.classIndex<0) { dlSymbolOptions.classIndex=0; @@ -161,6 +235,7 @@ public class DLSymbolModifier extends SymbolModifier { * Get the JavaFX symbol options pane that has options for the symbol pane. * @return the symbol options pane. */ + @Override public SymbolModifierPane getOptionsPane() { if (optionsPane == null) { optionsPane = new DLSymbolOptionPane(this); @@ -168,6 +243,23 @@ public class DLSymbolModifier extends SymbolModifier { return optionsPane; } + @Override + public PamDialogPanel getDialogPanel() { + if (optionsPanel == null) { + optionsPanel = new DLSymbolOptionPanel(this); + } + return optionsPanel; + } + + /** + * Default behaviour to show the dialog panel. + * @param e + * @param dialogPanel + */ + protected void showOptionsDialog(ActionEvent e, PamDialogPanel dialogPanel) { + GenericSwingDialog.showDialog(PamController.getMainFrame(), getName() + " options", dialogPanel); + } + public DLAnnotationType getDLAnnotType() { return dlAnnotType; diff --git a/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolModifierParams.java b/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolModifierParams.java index 718b6378..45e775a8 100644 --- a/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolModifierParams.java +++ b/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolModifierParams.java @@ -1,7 +1,10 @@ package rawDeepLearningClassifier.dataPlotFX; + import PamView.symbol.modifier.SymbolModifierParams; +import javafx.scene.paint.Color; import pamViewFX.fxNodes.utilsFX.ColourArray.ColourArrayType; +import pamViewFX.fxNodes.utilsFX.PamUtilsFX; /** * Parameters for colouring symbols by deep learning probability. @@ -10,11 +13,28 @@ import pamViewFX.fxNodes.utilsFX.ColourArray.ColourArrayType; * */ public class DLSymbolModifierParams extends SymbolModifierParams { + + public DLSymbolModifierParams() { + setDefaultClassColors(32); + } /** * */ - private static final long serialVersionUID = 1L; + private static final long serialVersionUID = 3L; + + + public static final int PREDICITON_COL = 0; + + public static final int CLASS_COL = 1; + + /** + * The way to colour the predictions. + */ + public int colTypeSelection = PREDICITON_COL; + + + /****Prediction colours****/ /** * The colour limits. @@ -27,16 +47,72 @@ public class DLSymbolModifierParams extends SymbolModifierParams { */ public int classIndex = 0; + + /*******Class colours******/ + /** - * Show only binary. + * The current colours for each */ - public boolean showOnlyBinary = false; - + public int[] classColors = new int[32]; + + /** + * The currently selected class for colour picker- just so the user sees the same selection. + */ + public int classIndex2 = 0; + /** * The colour array to show. */ public ColourArrayType colArray = ColourArrayType.FIRE; + /***************************/ + + + /** + * Show only detections which have passed a decision threshold. + */ + public boolean showOnlyBinary = false; + + /** + * Set the default colours. + * @param num - the number of colours to set. + */ + public void setDefaultClassColors(int num) { + if (classColors==null || classColors.length classNameBox2; + + /** + * Pane which holds controls for changing the colour based on prediciton value + */ + private Pane probPane; + + /** + * Pane which holds controls for changing the colour based on the highest prediction value + */ + private Pane classPane; + + + private PamBorderPane holder; + + /** + * Button to select how to colour. + */ + private ToggleButton b1, b2; public DLSymbolOptionPane(SymbolModifier symbolModifer) { super(symbolModifer, Orientation.HORIZONTAL, true, 0); - this.setBottom(createProbPane()); + + probPane = createProbPane(); + classPane = createClassPane(); + classPane.setPadding(new Insets(0,0,5,0)); + + b1 = new ToggleButton("Prediction"); + b1.setPrefWidth(80); +// b1.setStyle("-fx-border-radius: 5 0 0 5; -fx-background-radius: 5 0 0 5;"); + + b2 = new ToggleButton("Class"); + b2.setPrefWidth(80); + + b1.setOnAction((a)->{ + setSettingsPane(); + //change the colour of the colour range slider. + notifySettingsListeners(); + }); +// b2.setStyle("-fx-border-radius: 0 5 5 0; -fx-background-radius: 0 5 5 0;"); + + + b2.setOnAction((a)->{ + setSettingsPane(); + //change the colour of the colour range slider. + notifySettingsListeners(); + }); + + SegmentedButton segmentedButton = new SegmentedButton(); + segmentedButton.getButtons().addAll(b1, b2); + segmentedButton.setPadding(new Insets(5,0,5,0)); + segmentedButton.getStyleClass().add(SegmentedButton.STYLE_CLASS_DARK); + + + BorderPane.setAlignment(segmentedButton, Pos.CENTER); + b1.setSelected(true); + + showOnlyBinary = new CheckBox("Show only binary classificaiton"); + showOnlyBinary.setOnAction((action)->{ + getParams(); + //change the colour of the colour range slider. + notifySettingsListeners(); + }); + showOnlyBinary.setTooltip(new Tooltip("Colour detections only if they passed decision threshold")); + + holder = new PamBorderPane(); + + holder.setTop(segmentedButton); + holder.setCenter(probPane); + holder.setBottom(showOnlyBinary); + + this.setBottom(holder); + this.dlSymbolModifier = (DLSymbolModifier) symbolModifer; initialised=true; } + + private void setSettingsPane() { + if (b1.isSelected()) { + holder.setCenter(probPane); + } + if (b2.isSelected()) { + holder.setCenter(classPane); + } + } + + + private Pane createClassPane() { + + classNameBox2 = new ComboBox(); + classNameBox2.setOnAction((action)->{ + getParams(); + //change the colour of the colour range slider. + notifySettingsListeners(); + }); + classNameBox2.setMaxWidth(Double.MAX_VALUE); + + classNameBox2.setOnAction((action)->{ + if (classNameBox2.getSelectionModel().getSelectedIndex()>=0){ + colourPicker.setValue(PamUtilsFX.intToColor(dlSymbolModifier.getSymbolModifierParams().classColors[classNameBox2.getSelectionModel().getSelectedIndex()])); + } + }); + + classNameBox2.setPrefWidth(CLASS_NAME_BOX_WIDTH); + + colourPicker = new ColorPicker(); + colourPicker.setPrefWidth(60); + colourPicker.setOnAction((action)->{ + getParams(); + notifySettingsListeners(); + }); + + HBox.setHgrow(classNameBox, Priority.ALWAYS); + + PamHBox classHolder = new PamHBox(); + classHolder.setSpacing(5); + classHolder.setAlignment(Pos.CENTER_LEFT); + classHolder.getChildren().addAll(classNameBox2, colourPicker); + + return classHolder; + + } + private Pane createProbPane() { @@ -82,6 +217,8 @@ public class DLSymbolOptionPane extends StandardSymbolModifierPane { //change the colour of the colour range slider. notifySettingsListeners(); }); + classNameBox.setPrefWidth(CLASS_NAME_BOX_WIDTH); + colourBox = new ColorComboBox(ColorComboBox.COLOUR_ARRAY_BOX); colourBox.setPrefWidth(50); @@ -92,17 +229,10 @@ public class DLSymbolOptionPane extends StandardSymbolModifierPane { colourRangeSlider.setColourArrayType( dlSymbolModifier.getSymbolModifierParams().colArray); }); - showOnlyBinary = new CheckBox("Show only binary classificaiton"); - showOnlyBinary.setOnAction((action)->{ - getParams(); - //change the colour of the colour range slider. - notifySettingsListeners(); - }); - PamHBox classHolder = new PamHBox(); classHolder.setSpacing(5); classHolder.setAlignment(Pos.CENTER_LEFT); - classHolder.getChildren().addAll(new Label("Show Class"), classNameBox); + classHolder.getChildren().addAll(new Label("Show class"), classNameBox); PamHBox colorHolder = new PamHBox(); colorHolder.setSpacing(5); @@ -112,7 +242,7 @@ public class DLSymbolOptionPane extends StandardSymbolModifierPane { holder.getChildren().addAll(classHolder, new Label("Probability"), colorHolder); - holder.getChildren().add(showOnlyBinary); +// holder.getChildren().add(showOnlyBinary); holder.setPadding(new Insets(5,0,5,0)); setParams = false; @@ -120,35 +250,68 @@ public class DLSymbolOptionPane extends StandardSymbolModifierPane { return holder; } - @Override - public StandardSymbolOptions getParams(){ - StandardSymbolOptions standardSymbolOptions = super.getParams(); + + /** + * get parameters for colouring by class. + * @param symbolOptions - the symbol options. + * @return + */ + public DLSymbolModifierParams getClassColParams(DLSymbolModifierParams symbolOptions ) { + + int index = classNameBox2.getSelectionModel().getSelectedIndex()>=0 ? classNameBox2.getSelectionModel().getSelectedIndex():0; - - //bit messy but works / - DLSymbolModifierParams symbolOptions = dlSymbolModifier.getSymbolModifierParams(); + symbolOptions.classColors[index] = PamUtilsFX.colorToInt(colourPicker.getValue()); + + symbolOptions.classIndex2 = classNameBox2.getSelectionModel().getSelectedIndex(); + + return symbolOptions; + } + + /** + * + * @param symbolOptions + * @return + */ + public DLSymbolModifierParams getPredictionColParams(DLSymbolModifierParams symbolOptions ) { - //need to chekc this here. - //checkClassNamesBox(symbolOptions); - - symbolOptions.clims=new double[] {colourRangeSlider.getLowValue(), colourRangeSlider.getHighValue()}; symbolOptions.colArray = ColourArray.getColorArrayType(this.colourBox.getSelectionModel().getSelectedItem()); symbolOptions.classIndex = classNameBox.getSelectionModel().getSelectedIndex(); + return symbolOptions; + } + + + @Override + public StandardSymbolOptions getParams(){ + StandardSymbolOptions standardSymbolOptions = super.getParams(); + + //bit messy but works + DLSymbolModifierParams symbolOptions = dlSymbolModifier.getSymbolModifierParams(); + + //need to check this here. + //checkClassNamesBox(symbolOptions); + + if (b1.isSelected()) symbolOptions.colTypeSelection = DLSymbolModifierParams.PREDICITON_COL; + if (b2.isSelected()) symbolOptions.colTypeSelection = DLSymbolModifierParams.CLASS_COL; + + //get parameters for colouring + symbolOptions = getClassColParams(symbolOptions); + + //get parameters for colouring by prediction value + symbolOptions = getPredictionColParams(symbolOptions) ; + symbolOptions.showOnlyBinary = showOnlyBinary.isSelected(); dlSymbolModifier.checkColourArray(); - //System.out.println("Get params: " ); - return standardSymbolOptions; - } - private void checkClassNamesBox(DLSymbolModifierParams symbolOptions) { + + private int checkClassNamesBox(DLSymbolModifierParams symbolOptions, ComboBox classNameBox) { DLClassName[] classNames = dlSymbolModifier.getDLAnnotType().getDlControl().getDLModel().getClassNames(); @@ -167,8 +330,51 @@ public class DLSymbolOptionPane extends StandardSymbolModifierPane { classNameBox.getItems().add("Class: " + i); } } + + return nClass; + + } + + /** + * Set parameters for controls to change the colour gradient based on prediction. + * @param symbolOptions - the symbol options + */ + private void setPredictionColParams(DLSymbolModifierParams symbolOptions) { + + //now set frequency parameters + colourRangeSlider.setLowValue(symbolOptions.clims[0]); + colourRangeSlider.setHighValue(symbolOptions.clims[1]); + colourRangeSlider.setColourArrayType( symbolOptions.colArray); + + + int nClass = checkClassNamesBox( symbolOptions, classNameBox); symbolOptions.classIndex = Math.min(symbolOptions.classIndex, nClass-1); classNameBox.getSelectionModel().select(Math.max(symbolOptions.classIndex, 0)); + + //color box. + colourBox.setValue(symbolOptions.colArray); + } + + /** + * Set parameters for controls to change the colour gradient based on prediction. + * @param symbolOptions - the symbol options + */ + private void setClassColParams(DLSymbolModifierParams symbolOptions) { + + int nClass = checkClassNamesBox( symbolOptions, classNameBox2); + + symbolOptions.classIndex = Math.min(symbolOptions.classIndex, nClass-1); + classNameBox2.getSelectionModel().select(Math.max(symbolOptions.classIndex2, 0)); + + + int index = symbolOptions.classIndex2>=0 ? symbolOptions.classIndex2 : 0; + + if (symbolOptions.classColors==null) { + symbolOptions.setDefaultClassColors(nClass); + } + + //set the correct colour + colourPicker.setValue(PamUtilsFX.intToColor(symbolOptions.classColors[index])); } @Override @@ -180,24 +386,27 @@ public class DLSymbolOptionPane extends StandardSymbolModifierPane { setParams = true; -// StandardSymbolOptions standardSymbolOptions = (StandardSymbolOptions) getSymbolModifier().getSymbolChooser().getSymbolOptions(); - + //get the symbool options DLSymbolModifierParams symbolOptions = dlSymbolModifier.getSymbolModifierParams(); - //now set frequency parameters - colourRangeSlider.setLowValue(symbolOptions.clims[0]); - colourRangeSlider.setHighValue(symbolOptions.clims[1]); - colourRangeSlider.setColourArrayType( symbolOptions.colArray); - - //set the combo box class types. - checkClassNamesBox( symbolOptions); +// b1.setSelected(false); +// b2.setSelected(false); + if (symbolOptions.colTypeSelection == DLSymbolModifierParams.PREDICITON_COL) b1.setSelected(true); + if (symbolOptions.colTypeSelection == DLSymbolModifierParams.CLASS_COL) b2.setSelected(true); + setSettingsPane(); - //color box. - colourBox.setValue(symbolOptions.colArray); +// symbolOptions.colTypeSelection = b1.isSelected() ? DLSymbolModifierParams.PREDICITON_COL : DLSymbolModifierParams.CLASS_COL; + + //set the parameters for colouring by prediction + setPredictionColParams(symbolOptions); + + //set the class colour parameters + setClassColParams(symbolOptions); //set the selected. showOnlyBinary.setSelected(symbolOptions.showOnlyBinary); + setParams = false; } diff --git a/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolOptionPanel.java b/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolOptionPanel.java new file mode 100644 index 00000000..3ef63655 --- /dev/null +++ b/src/rawDeepLearningClassifier/dataPlotFX/DLSymbolOptionPanel.java @@ -0,0 +1,424 @@ +package rawDeepLearningClassifier.dataPlotFX; + +import java.awt.BorderLayout; +import java.awt.Color; +import java.awt.Dimension; +import java.awt.FlowLayout; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.event.ActionEvent; +import java.awt.event.ActionListener; + +import javax.swing.AbstractButton; +import javax.swing.BorderFactory; +import javax.swing.Box; +import javax.swing.BoxLayout; +import javax.swing.ButtonGroup; +import javax.swing.JCheckBox; +import javax.swing.JColorChooser; +import javax.swing.JComboBox; +import javax.swing.JComponent; +import javax.swing.JFrame; +import javax.swing.JLabel; +import javax.swing.JPanel; +import javax.swing.JSlider; +import javax.swing.JToggleButton; +import javax.swing.border.TitledBorder; + +import PamView.ColourComboBox; +import PamView.PamAWTUtils; +import PamView.dialog.GenericSwingDialog; +import PamView.dialog.PamButton; +import PamView.dialog.PamDialogPanel; +import PamView.panel.PamPanel; +import PamView.symbol.StandardSymbolOptions; +import PamView.symbol.modifier.SymbolModifier; +import Spectrogram.ColourRangeSlider; +import pamViewFX.fxNodes.utilsFX.ColourArray; +import pamViewFX.fxNodes.utilsFX.PamUtilsFX; +import rawDeepLearningClassifier.dlClassification.DLClassName; + +/** + * + * Swing symbol options for the annotation pane. + * + * @author Jamie Macaulay + */ +public class DLSymbolOptionPanel implements PamDialogPanel, ActionListener { + + private static final long serialVersionUID = 1L; + + private static final double CLASS_NAME_BOX_WIDTH = 130; + + private DLSymbolModifier dlSymbolModifier; + + /** + * The color range slider for coloring probabilities. + */ + private ColourRangeSlider colorRangeSlider; + + /** + * The combo box allowing users to select which class to show. + */ + private JComboBox classNameBox; + private JComboBox classNameBox2; + + /** + * Check box allowing users only to show only those detections which have passed binary classification. + */ + private JCheckBox showOnlyBinary; + + /** + * Color picker which allows a user to select the gradient for coloring predictions + */ + private ColourComboBox colorComboBox; + + /** + * Color picker which allows a user to select color for each class. + */ + private JColorChooser colorPicker; + + private boolean initialized = true; + + /** + * Pane which holds controls for changing the colour based on prediciton value + */ + private JPanel probPane; + + /** + * Pane which holds controls for changing the colour based on the highest prediction value + */ + private JPanel classPane; + + private PamPanel holder; + + /** + * Button to select how to colour. + */ + private JToggleButton b1, b2; + + private PamPanel mainPanel; + + private int[] classColours; + + private AbstractButton colourButton; + + + public DLSymbolOptionPanel(SymbolModifier symbolModifer) { + this.dlSymbolModifier = (DLSymbolModifier) symbolModifer; + + probPane = createProbPane(); + probPane.setBorder(new TitledBorder("Colour by prediction value")); + + classPane = createClassPane(); + classPane.setBorder(new TitledBorder("Colour by class")); + + b1 = new JToggleButton("Prediction"); + b1.setPreferredSize(new Dimension(100, 25)); // Set preferred size + + b2 = new JToggleButton("Class"); + b2.setPreferredSize(new Dimension(100, 25)); + + ButtonGroup buttonGroup = new ButtonGroup(); // Group toggle buttons + buttonGroup.add(b1); + buttonGroup.add(b2); + + b1.addActionListener(this); + b2.addActionListener(this); + + JPanel segmentedButtonPanel = new JPanel(new FlowLayout(FlowLayout.CENTER)); + segmentedButtonPanel.add(b1); + segmentedButtonPanel.add(b2); + + holder = new PamPanel(); + holder.setLayout(new BorderLayout()); + holder.add(new JLabel("Hello"), BorderLayout.NORTH); + + mainPanel = new PamPanel(); + mainPanel.setLayout(new BorderLayout()); + + mainPanel.add(segmentedButtonPanel, BorderLayout.NORTH); + mainPanel.add(holder, BorderLayout.CENTER); + mainPanel.add(showOnlyBinary = new JCheckBox("Show only binary classification"), BorderLayout.SOUTH); + + setSettingsPane(); + + initialized = true; + } + + @Override + public JComponent getDialogComponent() { + return mainPanel; + } + + @Override + public void setParams() { + //get the symbool options + DLSymbolModifierParams symbolOptions = dlSymbolModifier.getSymbolModifierParams(); + + // b1.setSelected(false); + // b2.setSelected(false); + if (symbolOptions.colTypeSelection == DLSymbolModifierParams.PREDICITON_COL) b1.setSelected(true); + if (symbolOptions.colTypeSelection == DLSymbolModifierParams.CLASS_COL) b2.setSelected(true); + + setSettingsPane(); + + //set the parameters for colouring by prediction + setPredictionColParams(symbolOptions); + + //set the class colour parameters + setClassColParams(symbolOptions); + + //set the selected. + showOnlyBinary.setSelected(symbolOptions.showOnlyBinary); + + setSettingsPane(); + } + + + private int checkClassNamesBox(DLSymbolModifierParams symbolOptions, JComboBox classNameBox) { + + DLClassName[] classNames = dlSymbolModifier.getDLAnnotType().getDlControl().getDLModel().getClassNames(); + + // for (int i =0; ii) { + classNameBox.addItem(classNames[i].className); + } + else { + classNameBox.addItem("Class: " + i); + } + } + + return nClass; + + } + + + /** + * Set parameters for controls to change the colour gradient based on prediction. + * @param symbolOptions - the symbol options + */ + private void setPredictionColParams(DLSymbolModifierParams symbolOptions) { + + //now set frequency parameters + colorRangeSlider.setValue((int) symbolOptions.clims[0]*100); + colorRangeSlider.setUpperValue((int) symbolOptions.clims[1]*100); + // colorRangeSlider.setColourArrayType( symbolOptions.colArray); + + colorRangeSlider.setColourMap(PamUtilsFX.fxColArray2Swing(symbolOptions.colArray)); + + + int nClass = checkClassNamesBox( symbolOptions, classNameBox); + + symbolOptions.classIndex = Math.min(symbolOptions.classIndex, nClass-1); + classNameBox.setSelectedIndex(Math.max(symbolOptions.classIndex, 0)); + + //color box. + colorComboBox.setSelectedColourMap(PamUtilsFX.fxColArray2Swing(symbolOptions.colArray)); + } + + /** + * Set parameters for controls to change the colour gradient based on prediction. + * @param symbolOptions - the symbol options + */ + private void setClassColParams(DLSymbolModifierParams symbolOptions) { + + //create a temporary array to save different class colours in - this needs to be + //before other params are set. + this.classColours = symbolOptions.classColors; + + + int nClass = checkClassNamesBox( symbolOptions, classNameBox2); + + symbolOptions.classIndex = Math.min(symbolOptions.classIndex, nClass-1); + + classNameBox2.setSelectedIndex(Math.max(symbolOptions.classIndex2, 0)); + + +// int index = symbolOptions.classIndex2>=0 ? symbolOptions.classIndex2 : 0; + + if (symbolOptions.classColors==null) { + symbolOptions.setDefaultClassColors(nClass); + } + + if (classNameBox2.getSelectedIndex()>=0) { + colourButton.setBackground(PamAWTUtils.intToColor(classColours[classNameBox2.getSelectedIndex()])); + colourButton.repaint(); + } + + + // //set the correct colour + // colorPicker.setColor(symbolOptions.classColors[index]); + } + + + /** + * get parameters for colouring by class. + * @param symbolOptions - the symbol options. + * @return + */ + public DLSymbolModifierParams getClassColParams(DLSymbolModifierParams symbolOptions ) { + + // int index = classNameBox2.getSelectedIndex()>=0 ? classNameBox2.getSelectedIndex():0; + + symbolOptions.classColors = classColours; + + symbolOptions.classIndex2 = classNameBox2.getSelectedIndex(); + + return symbolOptions; + } + + /** + * + * @param symbolOptions + * @return + */ + public DLSymbolModifierParams getPredictionColParams(DLSymbolModifierParams symbolOptions ) { + + symbolOptions.clims=new double[] {((double) colorRangeSlider.getValue())/100., ((double) colorRangeSlider.getUpperValue())/100.}; + + symbolOptions.colArray = PamUtilsFX.swingColArray2FX(this.colorComboBox.getSelectedColourMap()); + + symbolOptions.classIndex = classNameBox.getSelectedIndex(); + + return symbolOptions; + } + + @Override + public boolean getParams() { + + //bit messy but works + DLSymbolModifierParams symbolOptions = dlSymbolModifier.getSymbolModifierParams().clone(); + + //need to check this here. + //checkClassNamesBox(symbolOptions); + + if (b1.isSelected()) symbolOptions.colTypeSelection = DLSymbolModifierParams.PREDICITON_COL; + if (b2.isSelected()) symbolOptions.colTypeSelection = DLSymbolModifierParams.CLASS_COL; + + //get parameters for colouring + symbolOptions = getClassColParams(symbolOptions); + + //get parameters for colouring by prediction value + symbolOptions = getPredictionColParams(symbolOptions) ; + + symbolOptions.showOnlyBinary = showOnlyBinary.isSelected(); + + dlSymbolModifier.checkColourArray(); + + //set the paratmers. + dlSymbolModifier.setSymbolModifierParams(symbolOptions); + + return true; + } + + + private void setSettingsPane() { + holder.removeAll(); + //holder.setLayout(new BorderLayout()); + holder.validate(); + + if (b1.isSelected()) { + holder.add(probPane, BorderLayout.CENTER); + } else if (b2.isSelected()) { + holder.add(classPane, BorderLayout.CENTER); + } + + holder.validate(); + mainPanel.validate(); + + if (mainPanel.getRootPane()!=null) { + //pack the dialog because it is a different size + ((GenericSwingDialog) mainPanel.getRootPane().getParent()).pack(); + } + } + + + private JPanel createClassPane() { + + classNameBox2 = new JComboBox<>(); + classNameBox2.addActionListener((action)->{ + //make sure the setting button shows the colour + if (classNameBox2.getSelectedIndex()>=0) { + colourButton.setBackground(PamAWTUtils.intToColor(classColours[classNameBox2.getSelectedIndex()])); + } + setSettingsPane(); + }); + + classNameBox2.setPreferredSize(new Dimension((int) CLASS_NAME_BOX_WIDTH, 25)); + + // colorPicker.setPreferredSize(new Dimension(60, 25)); + colourButton = new PamButton("Color"); + colourButton.addActionListener((action)->{ + Color color = JColorChooser.showDialog(colourButton, "Pick colour for class", PamAWTUtils.intToColor(classColours[classNameBox2.getSelectedIndex()])); + if (color!=null) { + colourButton.setBackground(color); + // colourButton.setForeground(color); + classColours[classNameBox2.getSelectedIndex()]=PamAWTUtils.colorToInt(color); + } + }); + + JPanel classHolder = new JPanel(); + FlowLayout flowLayout = new FlowLayout(); + classHolder.setLayout(flowLayout); + + classHolder.add(classNameBox2); + classHolder.add(colourButton); + + return classHolder; + } + + + private PamPanel createProbPane() { + PamPanel holder = new PamPanel(); + holder.setLayout(new GridBagLayout()); + + GridBagConstraints c = new GridBagConstraints(); + + c.gridx = 0; + c.gridy = 0; + + holder.add(new JLabel("Select class"), c); + + c.gridx++; + classNameBox = new JComboBox(); + holder.add(classNameBox, c); + + c.gridx = 0; + c.gridwidth = 1; + c.gridy++; + holder.add(new JLabel("Color map"), c); + + c.gridx++; + colorComboBox = new ColourComboBox(); + colorComboBox.addActionListener((action)->{ + colorRangeSlider.setColourMap(colorComboBox.getSelectedColourMap()); + colorRangeSlider.repaint(); + }); + + holder.add(colorComboBox, c); + + c.gridx = 0; + c.gridy++; + c.gridwidth =2; + colorRangeSlider = new ColourRangeSlider(0, 100, JSlider.HORIZONTAL); // Min 0, Max 1 for probabilities + colorRangeSlider.setPaintTicks(true); + colorRangeSlider.setMinorTickSpacing(20); + holder.add(colorRangeSlider, c); + + return holder; + } + + @Override + public void actionPerformed(ActionEvent e) { + setSettingsPane(); + } + +} diff --git a/src/rawDeepLearningClassifier/dataSelector/DLDataFilter.java b/src/rawDeepLearningClassifier/dataSelector/DLDataFilter.java new file mode 100644 index 00000000..7ba90b5c --- /dev/null +++ b/src/rawDeepLearningClassifier/dataSelector/DLDataFilter.java @@ -0,0 +1,49 @@ +package rawDeepLearningClassifier.dataSelector; + +import PamView.dialog.PamDialogPanel; +import PamguardMVC.PamDataUnit; +import PamguardMVC.dataSelector.DataSelectParams; +import pamViewFX.fxSettingsPanes.DynamicSettingsPane; + +/** + * Score a data unit with a deep learning annotation. Note that this could be an + * external data unit with an annotation e.g. a click, or deep learning + * detections generated by the deep learning module. + * + * @author Jamie Macaulay + */ +public interface DLDataFilter { + + /** + * Score a data unit with deep learning annotations + * @param dataUnit - the data unit to score + * @return 0 to reject and >0 to accept. + */ + public int scoreDLData(PamDataUnit dataUnit); + + /** + * Get parameters for the data filter. + * @return parameters for the data selectors. + */ + public DataSelectParams getParams(); + + /** + * Set the parameters for the data filter. + * @param params - the parameters to set. + */ + public void setParams(DataSelectParams params); + + /** + * Settings controls for the deep learning filter. + * @return the controls for this filter. + */ + public DynamicSettingsPane getSettingsPane(); + + /** + * Swing settings controls for the data deep learning filter. + * @return Swing dialog for the settings + */ + public PamDialogPanel getSettingsPanel(); + + +} diff --git a/src/rawDeepLearningClassifier/dataSelector/DLDataSelectCreator.java b/src/rawDeepLearningClassifier/dataSelector/DLDataSelectCreator.java new file mode 100644 index 00000000..e484aa86 --- /dev/null +++ b/src/rawDeepLearningClassifier/dataSelector/DLDataSelectCreator.java @@ -0,0 +1,40 @@ +package rawDeepLearningClassifier.dataSelector; + +import PamguardMVC.dataSelector.DataSelectParams; +import PamguardMVC.dataSelector.DataSelector; +import annotation.DataAnnotationType; +import annotation.dataselect.AnnotationDataSelCreator; +import rawDeepLearningClassifier.DLControl; +import rawDeepLearningClassifier.logging.DLAnnotation; +import rawDeepLearningClassifier.logging.DLAnnotationType; + +/** + * Creates a data selector for the deep learning module. + * + * @author Jamie Macaulay + */ +public class DLDataSelectCreator extends AnnotationDataSelCreator { + + private DLControl dlcontrol; + + public DLDataSelectCreator(DLControl dlcontrol, DLAnnotationType type) { + super(type); + this.dlcontrol = dlcontrol; + } + + @Override + public DataSelectParams createNewParams(String name) { + return new DLDataSelectorParams(); + } + + @Override + public DataSelector createDataSelector(DataAnnotationType dataAnnotationType, String selectorName, + boolean allowScores, String selectorType) { + // TODO Auto-generated method stub + return new DLDataSelector(dlcontrol, dataAnnotationType, null, + selectorName, allowScores); + } + + + +} diff --git a/src/rawDeepLearningClassifier/dataSelector/DLDataSelector.java b/src/rawDeepLearningClassifier/dataSelector/DLDataSelector.java new file mode 100644 index 00000000..e4858e5f --- /dev/null +++ b/src/rawDeepLearningClassifier/dataSelector/DLDataSelector.java @@ -0,0 +1,153 @@ +package rawDeepLearningClassifier.dataSelector; + +import java.util.ArrayList; +import java.util.List; + +import PamView.dialog.PamDialogPanel; +import PamguardMVC.PamDataBlock; +import PamguardMVC.PamDataUnit; +import PamguardMVC.dataSelector.DataSelectParams; +import PamguardMVC.dataSelector.DataSelector; +import annotation.DataAnnotationType; +import annotation.dataselect.AnnotationDataSelector; +import javafx.scene.Node; +import pamViewFX.fxSettingsPanes.DynamicSettingsPane; +import rawDeepLearningClassifier.DLControl; +import rawDeepLearningClassifier.logging.DLAnnotation; + +/** + * Data selector of DL data units. Note that data selectors are for deep leanring annotations + * rather than deep learning data units. + *

+ * The data selector can have different types of data selectors which can + * depend on the classifier used and user choice. + *

+ * Note that this is slightly different from DLPredicitoDecision + * as it deals with data units that may have a more than one prediction. + * i.e. + */ +public class DLDataSelector extends AnnotationDataSelector { + + + /** + * Swing panel for the data selector. + */ + private DLSelectPanel dlSelectPanel; + + + /** + * FX panel for the data selector. + */ + private DLSelectPaneFX dlSelectPaneFX; + + /** + * Data filter for filtering. + */ + private List dataFilters = new ArrayList (); + + + private DLDataSelectorParams dlDataSelectParams; + + /** + * Constructor for the data selector. + * @param dlcontrol - reference to the DLControl. + * @param pamDataBlock - the data block. + * @param selectorName - the selector name. + * @param allowScores - allow all the scores. + * @param selectorType - the selector type. + */ + public DLDataSelector(DLControl dlcontrol, DataAnnotationType annotationType, PamDataBlock pamDataBlock, + String selectorName, boolean allowScores) { + super(annotationType, pamDataBlock, selectorName, allowScores); + /****New data filters go here****/ + dataFilters.add(new DLPredictionFilter(dlcontrol)); + + //create default params + dlDataSelectParams = new DLDataSelectorParams(); + dlDataSelectParams.dataSelectorParams = new DataSelectParams[dataFilters.size()]; + for (int i=0; i getDialogPaneFX() { + if (dlSelectPaneFX == null) { + dlSelectPaneFX = new DLSelectPaneFX(this); + } + return dlSelectPaneFX; + } + + @Override + public void setParams(DataSelectParams dataSelectParams) { + + if (dataSelectParams instanceof DLDataSelectorParams) { + dlDataSelectParams = (DLDataSelectorParams) dataSelectParams; + + checkDataFilterParamsSize(dlDataSelectParams); + + //set paramters for all data filters. + for (int i=0; i=0 ? 1 : 0; + } + + @Override + protected double scoreData(PamDataUnit pamDataUnit, DLAnnotation annotation) { + int score = dataFilters.get(dlDataSelectParams.dataSelectorIndex).scoreDLData(pamDataUnit); + + //the score is the index of the class that scores highest or -1 if it does not pass threshold prediciton. + //Need to make more simple here as scores in PG are 0 for not passed rather than negative. + + return score>=0 ? 1 : 0; + } + + public DLDataFilter getCurrentDataSelector() { + return dataFilters.get(dlDataSelectParams.dataSelectorIndex); + } + + public List getDataSelectors() { + return dataFilters; + } + +} diff --git a/src/rawDeepLearningClassifier/dataSelector/DLDataSelectorParams.java b/src/rawDeepLearningClassifier/dataSelector/DLDataSelectorParams.java new file mode 100644 index 00000000..ebbc48c1 --- /dev/null +++ b/src/rawDeepLearningClassifier/dataSelector/DLDataSelectorParams.java @@ -0,0 +1,22 @@ +package rawDeepLearningClassifier.dataSelector; + +import java.io.Serializable; + +import PamguardMVC.dataSelector.DataSelectParams; + +/** + * Paramters for the DL data seelctor + */ +public class DLDataSelectorParams extends DataSelectParams implements Serializable, Cloneable{ + + private static final long serialVersionUID = 1L; + + public int dataSelectorIndex = 0; + + /** + * List of data selector parameters for different deep learning selectors. + */ + public DataSelectParams[] dataSelectorParams; + + +} diff --git a/src/rawDeepLearningClassifier/dataSelector/DLPredictionFilter.java b/src/rawDeepLearningClassifier/dataSelector/DLPredictionFilter.java new file mode 100644 index 00000000..403c0d45 --- /dev/null +++ b/src/rawDeepLearningClassifier/dataSelector/DLPredictionFilter.java @@ -0,0 +1,140 @@ +package rawDeepLearningClassifier.dataSelector; + + +import PamView.dialog.PamDialogPanel; +import PamguardMVC.PamDataUnit; +import PamguardMVC.dataSelector.DataSelectParams; +import pamViewFX.fxSettingsPanes.DynamicSettingsPane; +import rawDeepLearningClassifier.DLControl; +import rawDeepLearningClassifier.dlClassification.DLDetection; +import rawDeepLearningClassifier.dlClassification.PredictionResult; +import rawDeepLearningClassifier.logging.DLAnnotation; + +/** + * A data filter which filters data by the maximum prediction value + * for different classes. + * + * @author Jamie Macaulay + */ +public class DLPredictionFilter implements DLDataFilter { + + /** + * Reference to the DLControl + */ + private DLControl dlcontrol; + + /** + * The filter parameters + */ + private DLPredictionFilterParams filterParams = new DLPredictionFilterParams(); + + private DLPredictonPane dlPredictonPane; + + private DLPredictionPanel dlPredictonPanel; + + public DLPredictionFilter(DLControl dlcontrol) { + this.dlcontrol = dlcontrol; + checkParamsClass() ; + } + + + @Override + public int scoreDLData(PamDataUnit dataUnit) { + + + DLAnnotation annotation = (DLAnnotation) dataUnit. findDataAnnotation(DLAnnotation.class) ; + + if (annotation==null) return -1; + + //iterate through all results and check that at least one class passes data selection. + float[] results; + int maxClassIndex = -1; + int maxPred = -1; + + //get the maximum prediction index which passes the minimum threshold + for (PredictionResult modelResult: annotation.getModelResults()) { + results = modelResult.getPrediction(); + for (int j=0; jfilterParams.minClassPredicton[j]) { + if (results[j]> maxPred) maxClassIndex = j; + } + } + } + + return maxClassIndex; + } + + @Override + public DLPredictionFilterParams getParams() { + checkParamsClass() ; + return filterParams; + } + + @Override + public void setParams(DataSelectParams params) { + this.filterParams = ((DLPredictionFilterParams) params).clone(); + checkParamsClass(); + + } + + private void checkParamsClass() { + if (filterParams.classSelect==null || dlcontrol.getNumClasses()!=filterParams.classSelect.length) { + filterParams.classSelect = new boolean[dlcontrol.getNumClasses()]; + filterParams.minClassPredicton = new double[dlcontrol.getNumClasses()]; + + //set default so data selector does not always prevent all classes from showing. + for (int i=0; i predictions) { +// +// float[][] results = new float[predictions.size()][]; +// +// //A detection might have multiple prediction results, i.e. predictions are a matrix. Need +// //to iterate through all the predictions and then work out whihc is the maximum. That index is then then] +// //class colour. +// int i=0; +// for (PredictionResult modelResult: predictions) { +// results[i] = modelResult.getPrediction(); +// i++; +// } +// +// int[] indexBest = PamArrayUtils.maxPos(results); +// +// return indexBest; +// } + +} diff --git a/src/rawDeepLearningClassifier/dataSelector/DLPredictionFilterParams.java b/src/rawDeepLearningClassifier/dataSelector/DLPredictionFilterParams.java new file mode 100644 index 00000000..853849b6 --- /dev/null +++ b/src/rawDeepLearningClassifier/dataSelector/DLPredictionFilterParams.java @@ -0,0 +1,36 @@ +package rawDeepLearningClassifier.dataSelector; + +import PamguardMVC.dataSelector.DataSelectParams; + +/** + * Parameters for filtering by the minimum prediciton value. + */ +public class DLPredictionFilterParams extends DataSelectParams implements Cloneable{ + + private static final long serialVersionUID = 1L; + + /** + * An array indicating which classes are to be used in data selection + */ + public boolean[] classSelect; + + /** + * The minimum class prediction. + */ + public double[] minClassPredicton; + + /** + * Clone the parameters. + */ + @Override + public DLPredictionFilterParams clone() { + try { + return (DLPredictionFilterParams) super.clone(); + } catch (CloneNotSupportedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return null; + } + +} diff --git a/src/rawDeepLearningClassifier/dataSelector/DLPredictionPanel.java b/src/rawDeepLearningClassifier/dataSelector/DLPredictionPanel.java new file mode 100644 index 00000000..5af3ee78 --- /dev/null +++ b/src/rawDeepLearningClassifier/dataSelector/DLPredictionPanel.java @@ -0,0 +1,183 @@ +package rawDeepLearningClassifier.dataSelector; + +import java.awt.Color; +import java.awt.Dimension; +import java.awt.GridBagConstraints; +import java.awt.GridBagLayout; +import java.awt.Label; + +import javax.swing.JCheckBox; +import javax.swing.JComponent; +import javax.swing.JSpinner; +import javax.swing.JToggleButton; +import javax.swing.SpinnerNumberModel; +import javax.swing.event.ChangeEvent; +import javax.swing.event.ChangeListener; + +import org.kordamp.ikonli.materialdesign2.MaterialDesignL; +import org.kordamp.ikonli.swing.FontIcon; + +import PamView.dialog.PamDialogPanel; +import PamView.dialog.PamGridBagContraints; +import PamView.panel.PamPanel; +import rawDeepLearningClassifier.dlClassification.DLClassName; + +/** + * Swing panel for Deep learning predicitons. + */ +public class DLPredictionPanel implements PamDialogPanel { + + private DLPredictionFilter predicitonFilter; + + private PamPanel contentPanel; + + private JCheckBox[] enableClass; + + private JSpinner[] spinnerClass; + + private JToggleButton lockButton; + + + public DLPredictionPanel(DLPredictionFilter dlPredictionFilter) { + super(); + this.predicitonFilter = dlPredictionFilter; + + contentPanel = new PamPanel(); + contentPanel.setLayout(new GridBagLayout()); + + lockButton = new JToggleButton(); + + FontIcon iconlock = FontIcon.of(MaterialDesignL.LOCK); + iconlock.setIconSize(20); + iconlock.setIconColor(Color.DARK_GRAY); + + FontIcon iconlockopen = FontIcon.of(MaterialDesignL.LOCK_OPEN); + iconlockopen.setIconSize(20); + iconlockopen.setIconColor(Color.DARK_GRAY); + + lockButton.setIcon(iconlockopen); + + lockButton.addActionListener((action)->{ + if (lockButton.isSelected()) { + lockButton.setIcon(iconlock); + } + else { + lockButton.setIcon(iconlockopen); + } + lockButton.validate(); + }); + + + } + + @Override + public JComponent getDialogComponent() { + return contentPanel; + } + + @Override + public void setParams() { + + DLPredictionFilterParams params = predicitonFilter.getParams(); + // TODO Auto-generated method stub + setClassPane(params); + + for (int i=0; i{ + spinnerClass[ii].setEnabled(enableClass[ii].isSelected()); + }); + enableClass[i].setToolTipText(classNames[i].className); + contentPanel.add(enableClass[i], c); + + c.gridx = 1; + + + spinnerClass[i] = new JSpinner(new SpinnerNumberModel(0., 0., 1., 0.05)); + + Dimension prefSize = spinnerClass[i].getPreferredSize(); + prefSize = new Dimension(60, prefSize.height); + spinnerClass[i] .setPreferredSize(prefSize); + + spinnerClass[i].addChangeListener(new ChangeListener() { + @Override + public void stateChanged(ChangeEvent e) { + if (lockButton.isSelected()) { + for (int j=0; j1) { + //set a lock button to + c.gridx=2; + contentPanel.add(lockButton, c); + + //make the lock button the same height as the spinner + Dimension prefSizeB = lockButton.getPreferredSize(); + lockButton.setPreferredSize(new Dimension(prefSizeB.width, prefSize.height)); + + } + + c.gridy++; + + + } + + contentPanel.validate(); + } + + @Override + public boolean getParams() { + + DLPredictionFilterParams currParams = predicitonFilter.getParams(); + + for (int i=0; i{ + + private DLPredictionFilter predicitonFilter; + + private PamBorderPane mainPane; + + private PamVBox contentPane; + + private ClassDataSelector[] classPanes; + + private ToggleButton toggelButton; + + public DLPredictonPane(DLPredictionFilter predicitonFilter) { + super(null); + this.predicitonFilter=predicitonFilter; + createPane(); + } + + + private void createPane() { + mainPane = new PamBorderPane(); + + PamBorderPane topPane = new PamBorderPane(); + + Label label = new Label("Show classes above min. prediction"); + PamBorderPane.setAlignment(label, Pos.BOTTOM_LEFT); +// label.setTextAlignment(TextAlignment.LEFT); + label.setAlignment(Pos.BOTTOM_LEFT); + + topPane.setLeft(label); + + toggelButton = new ToggleButton(); + toggelButton.setTooltip(new Tooltip("Lock sliders together so one slider changes all values")); + + toggelButton.setGraphic(PamGlyphDude.createPamIcon("mdi2l-lock-open", PamGuiManagerFX.iconSize)); + toggelButton.selectedProperty().addListener((onsVal, oldVal,newVal)->{ + if (newVal) { + toggelButton.setGraphic(PamGlyphDude.createPamIcon("mdi2l-lock", PamGuiManagerFX.iconSize)); + } + else { + toggelButton.setGraphic(PamGlyphDude.createPamIcon("mdi2l-lock-open", PamGuiManagerFX.iconSize)); + } + }); + toggelButton.setSelected(false); + + topPane.setRight(toggelButton); +// topPane.setAlignment(Pos.CENTER_RIGHT); + + contentPane = new PamVBox(); + contentPane.setSpacing(5); + + mainPane.setTop(topPane); + mainPane.setCenter(contentPane); + PamBorderPane.setMargin(topPane, new Insets(0,0,5.,0)); + } + + class ClassDataSelector extends PamHBox { + + Slider slider; + + CheckBox enable; + + Label valueLabel; + + private boolean enableListener = true; + + ClassDataSelector(String classType, int index) { + + enable = new CheckBox(classType); + enable.setPrefWidth(80); + enable.setTooltip(new Tooltip(classType)); + enable.setOnAction((a)->{ + //disable if the class is unticked. + slider.setDisable(!enable.isSelected()); + valueLabel.setDisable(!enable.isSelected()); + notifySettingsListeners(); + }); + + slider = new Slider(); + slider.setShowTickMarks(true); + slider.setMinorTickCount(10); + slider.setShowTickLabels(false); + slider.setMajorTickUnit(0.5); + + slider.valueProperty().addListener((obsval, oldval, newval)->{ + valueLabel.setText(String.format("%.2f", newval)); + + //if the lock button has been sleected then change all the sliders + //so that they are the same value as this slide (unless the class is disabled) + if (toggelButton.isSelected() && enableListener) { + for (int i=0; i + * Note that at the moment this only implements one type of data filter and so + * essentially all controls etc. for changing filters are invisible to the user. + * + * @author Jamie Macaulay + */ +public class DLSelectPaneFX extends DynamicSettingsPane{ + + private PamBorderPane mainPane; + + /** + * Reference to the deep learning data selector. + */ + private DLDataSelector dlDataSelector; + + /** + * The current index selected by the user - not in the params. + */ + private int currentIndex = 0; + + public DLSelectPaneFX(DLDataSelector dlDataSelector) { + super(null); + this.dlDataSelector=dlDataSelector; + + //there is currently one implemented fitler so no + //need for a comboBox etc. to select. + createPane(); + +// mainPane.getChildren().add(dlDataSelector.getDataSelectors().getSettingsPane().getContentNode()); + + } + + private void createPane() { + mainPane = new PamBorderPane(); + + //need to add a settings listener to the filter panes to pass on any notification this settings listener. + for (int i=0; i{ + //notify any listeners to this pane that a filter pane has changed. + notifySettingsListeners(); + }); + } + + } + + + @Override + public Boolean getParams(Boolean input) { +// System.out.println("Get params DL data selector!"); + DLDataSelectorParams currParams = dlDataSelector.getParams(); + + //TODO - maybe should grab settings from all filters or just the selected one? + currParams.dataSelectorParams[currentIndex] = dlDataSelector.getDataSelectors().get(currentIndex).getSettingsPane().getParams(currParams.dataSelectorParams[currentIndex]); + + dlDataSelector.setParams(currParams); + + return true; + } + + @Override + public void setParams(Boolean input) { + DLDataSelectorParams currParams = dlDataSelector.getParams(); + + this.currentIndex = currParams.dataSelectorIndex; + + dlDataSelector.getDataSelectors().get(currentIndex).getSettingsPane().setParams(currParams.dataSelectorParams[currentIndex]); + + setDataFilterPane(currentIndex); + + } + + private void setDataFilterPane(int index) { + DLDataFilter dlFilter = dlDataSelector.getDataSelectors().get(index); + mainPane.setCenter(dlFilter.getSettingsPane().getContentNode()); + } + + @Override + public String getName() { + return "Filter by deep learning result"; + } + + @Override + public Node getContentNode() { + return mainPane; + } + + @Override + public void paneInitialized() { + // TODO Auto-generated method stub + + } + +} diff --git a/src/rawDeepLearningClassifier/dataSelector/DLSelectPanel.java b/src/rawDeepLearningClassifier/dataSelector/DLSelectPanel.java new file mode 100644 index 00000000..642b2718 --- /dev/null +++ b/src/rawDeepLearningClassifier/dataSelector/DLSelectPanel.java @@ -0,0 +1,83 @@ +package rawDeepLearningClassifier.dataSelector; + +import java.awt.BorderLayout; + +import javax.swing.JComponent; +import javax.swing.JLabel; + +import PamView.dialog.PamDialogPanel; +import PamView.panel.PamPanel; +import javafx.scene.Node; +import pamViewFX.fxNodes.PamBorderPane; + +/** + * Swing panel for the deep learning data selector. + */ +public class DLSelectPanel implements PamDialogPanel { + + private PamPanel mainPanel; + + private DLDataSelector dlDataSelector; + + private int currentIndex = 0; + + public DLSelectPanel(DLDataSelector dlDataSelector) { + super(); + this.dlDataSelector=dlDataSelector; + + mainPanel = new PamPanel(); + mainPanel.setLayout(new BorderLayout()); + + } + + @Override + public JComponent getDialogComponent() { + return mainPanel; + } + + + private void setDataFilterPane(int index) { + DLDataFilter dlFilter = dlDataSelector.getDataSelectors().get(index); + mainPanel.add(dlFilter.getSettingsPanel().getDialogComponent(), BorderLayout.CENTER); + } + + @Override + public void setParams() { + DLDataSelectorParams currParams = dlDataSelector.getParams(); + + this.currentIndex = currParams.dataSelectorIndex; + + //set the stored paramters for the deep learning filter + dlDataSelector.getDataSelectors().get(currentIndex).setParams(currParams.dataSelectorParams[currentIndex]); + + //set the paramters in the dialog - note the dialog will have areference ot the filter and so can access the r + //set params above. + dlDataSelector.getDataSelectors().get(currentIndex).getSettingsPanel().setParams(); + + //set the + setDataFilterPane(currentIndex); + } + + @Override + public boolean getParams() { + + DLDataSelectorParams currParams = dlDataSelector.getParams(); + + //dialog has a reference to the data filter and will change params. + boolean dataFilterOK = dlDataSelector.getDataSelectors().get(currentIndex).getSettingsPanel().getParams(); + + if (dataFilterOK) { + //TODO - maybe should grab settings from all filters or just the selected one? + currParams.dataSelectorParams[currentIndex] = dlDataSelector.getDataSelectors().get(currentIndex).getParams(); + + dlDataSelector.setParams(currParams); + + return true; + } + else { + return false; + } + } + + +} diff --git a/src/rawDeepLearningClassifier/deep_learning_help.md b/src/rawDeepLearningClassifier/deep_learning_help.md index 3e14a2cd..25c589da 100644 --- a/src/rawDeepLearningClassifier/deep_learning_help.md +++ b/src/rawDeepLearningClassifier/deep_learning_help.md @@ -106,15 +106,15 @@ If using a deep learning model from a supported framework then all transforms ar _An AnimalSpot, Ketos or other supported deep learning model will automatically create a list of transforms with the appropriate settings. These is no need to use the advanced pane but it is there in case users wish to change transform settings for some reason_ -### Default Models +### Exanmple Models -Default models are selectable from the menu button in the Deep Learning Pane. Default models are deep learning classifiers which are open source, known to be effective and have have been published in open access academic litrature; they are downloaded directly from a GitHub repository and then all associated settings are automtically applied. The default model selection pane also contains hyperlinks to the papers descirbing each model which will take users directly to the relvent website. +Example models are selectable from the menu button in the Deep Learning Pane. Default models are deep learning classifiers which are open source, known to be effective and have have been published in open access academic litrature; they are downloaded directly from a GitHub repository and then all associated settings are automtically applied. The default model selection pane also contains hyperlinks to the papers descirbing each model which will take users directly to the relvent website.

-_Default models can be downloaded. Default models are models which are published, open and have been known to work well amongst the bioacoustics community. More will be added to PAMGaurd over time. If you you would like your model to become a defualt model then drop PAMGuard support an email._ +_Example models can be downloaded. Example models are models which are published, open and have been known to work well amongst the bioacoustics community. More will be added to PAMGaurd over time. If you you would like your model to become a defualt model then drop PAMGuard support an email._ ## Running @@ -144,6 +144,26 @@ _An example click detection module output coloured by deep learning annotations. Other displays also show outputs from the deep learning module. Hovering over data units in the click display will, for example, show deep learning prediction values. The spectrogram will also show deep learning detections as translucent blue boxes (these must be selected in the right click menu). +### Symbol Manager + +The deep learning classifier has a symbol manager that is used throughtout different PAMGuard displays. The symbol manager can either display deep learning detections by the prediction values of a particular class or colour the output class with the highest prediciton class. Colouring by a prediction value requires the user to select which output class they wish to display - all detections are then coloured by the output prediction values for that particular class via a selectable colour map. If colour by class is selected then each detection is coloured by the class with the maximum prediction value. The colours for each class are selectable. + +

+ +

+ +_The options for the deep learning symbol manager_ + +### Data selector + +The deep learning classifier has a data selector which is utilsied thorughout PAMGuard (for example for exporting detections, generating clips, showing on displays). The data selector allows users to filter detections based on a minimum prediciton values for different output classes. Classifiers with multiple classes can have different minimum predicitons values assigned to each class. The user interface allows user to use a slider to dynimically change the minimum prediciton value. The lock button clamps the minimum prediction values of all enabled output classes together. + +

+ +

+ +_The options for the deep learning data selector_ + ### MATLAB The easiest way to export to MATLAB is to select the desired units in the time base display, right click and select the MATLAB icon. Data units will be exported to a .mat file as list of structures which is then saved to the clipboard. This file can be saved and then dragged into MATLAB to open. diff --git a/src/rawDeepLearningClassifier/dlClassification/DLClassiferModel.java b/src/rawDeepLearningClassifier/dlClassification/DLClassiferModel.java index dd0e85b7..7575d80b 100644 --- a/src/rawDeepLearningClassifier/dlClassification/DLClassiferModel.java +++ b/src/rawDeepLearningClassifier/dlClassification/DLClassiferModel.java @@ -115,7 +115,7 @@ public interface DLClassiferModel { /** * Get the parameters which can be serialized from transforms. - * @param dlTransfroms- the dl transforms. + * @param dlTransfroms- the DL transforms. */ public static ArrayList getDLTransformParams(ArrayList dlTransfroms) { ArrayList dlTransformParams = new ArrayList(); diff --git a/src/rawDeepLearningClassifier/dlClassification/DLClassifyProcess.java b/src/rawDeepLearningClassifier/dlClassification/DLClassifyProcess.java index 4f487b5c..5efdee3e 100644 --- a/src/rawDeepLearningClassifier/dlClassification/DLClassifyProcess.java +++ b/src/rawDeepLearningClassifier/dlClassification/DLClassifyProcess.java @@ -415,7 +415,7 @@ public class DLClassifyProcess extends PamInstantProcess { else { /****Add annotation to existing data unit (e.g. click, clip or other RawDataHolder)****/ //Need to go by the parent data unit for merging data not the segments. Note that we may still add multiple - //predicitions to a single data unit depending on how many segments it contains. + //predictions to a single data unit depending on how many segments it contains. //System.out.println("New model data " + pamRawData.getParentDataUnit().getUID() + " " + groupDataBuffer[i].size() + " " + modelResultDataBuffer[i].size()); diff --git a/src/rawDeepLearningClassifier/dlClassification/DLDataUnit.java b/src/rawDeepLearningClassifier/dlClassification/DLDataUnit.java index 4fa03e38..ffd4e002 100644 --- a/src/rawDeepLearningClassifier/dlClassification/DLDataUnit.java +++ b/src/rawDeepLearningClassifier/dlClassification/DLDataUnit.java @@ -5,7 +5,7 @@ import PamguardMVC.PamDataUnit; import rawDeepLearningClassifier.dlClassification.genericModel.StandardPrediction; /** - * A data unit created from classification results of DL model. this data unit holds one model results, i.e. + * A data unit created from classification results of DL model. This data unit holds one model results, i.e. * corresponds to one segment of raw data. * * @author Jamie Macaulay diff --git a/src/rawDeepLearningClassifier/dlClassification/DLDetectionDataBlock.java b/src/rawDeepLearningClassifier/dlClassification/DLDetectionDataBlock.java index 490925cf..f0f4c43e 100644 --- a/src/rawDeepLearningClassifier/dlClassification/DLDetectionDataBlock.java +++ b/src/rawDeepLearningClassifier/dlClassification/DLDetectionDataBlock.java @@ -3,7 +3,10 @@ package rawDeepLearningClassifier.dlClassification; import PamView.GroupedDataSource; import PamView.GroupedSourceParameters; import PamguardMVC.AcousticDataBlock; +import PamguardMVC.dataSelector.DataSelectorCreator; +import clickTrainDetector.dataselector.CTDataSelectCreator; import rawDeepLearningClassifier.DLControl; +import rawDeepLearningClassifier.dataSelector.DLDataSelectCreator; import rawDeepLearningClassifier.tethys.DLSpeciesManager; import rawDeepLearningClassifier.tethys.DLTethysDataProvider; import tethys.TethysControl; @@ -18,10 +21,30 @@ import tethys.species.DataBlockSpeciesManager; */ public class DLDetectionDataBlock extends AcousticDataBlock implements GroupedDataSource { + /** + * Reference to the deep learning classifier process. + */ private DLClassifyProcess dlClassifyProcess; + + /** + * Reference to the Tethys data provider for the deep learning module (provides standardised metadata). + */ private DLTethysDataProvider dlTethysDataProvider; + + /** + * A species manager for metadata. + */ private DLSpeciesManager dlSpeciesManager; + + /** + * Reference to the deep learning control. + */ private DLControl dlControl; + + /** + * Data selector for the deep learning detections. + */ + private DLDataSelectCreator dlDataSelectCreator; public DLDetectionDataBlock(String dataName, DLClassifyProcess parentProcess, int channelMap) { super(DLDetection.class, dataName, parentProcess, channelMap); @@ -49,6 +72,19 @@ public class DLDetectionDataBlock extends AcousticDataBlock impleme } return dlSpeciesManager; } + + +// /* (non-Javadoc) +// * @see PamguardMVC.PamDataBlock#getDataSelectCreator() +// */ +// @Override +// public synchronized DataSelectorCreator getDataSelectCreator() { +// if (dlDataSelectCreator == null) { +// dlDataSelectCreator = new DLDataSelectCreator(dlControl, this); +// } +// return dlDataSelectCreator; +// +// } } diff --git a/src/rawDeepLearningClassifier/dlClassification/DLPredictionDecision.java b/src/rawDeepLearningClassifier/dlClassification/DLPredictionDecision.java new file mode 100644 index 00000000..1ba8555e --- /dev/null +++ b/src/rawDeepLearningClassifier/dlClassification/DLPredictionDecision.java @@ -0,0 +1,26 @@ +package rawDeepLearningClassifier.dlClassification; + +/** + * Decides whether a prediction has passed a threshold to be used to create a new + * data unit. + *

+ * Note that the majority of the time this will be a simple test of the value of + * predictions of a model but there will be cases when a classifier implements a + * more complex system. For example, a implementation could save a buffer of predictions + * so that previous predictions inform the latest prediction. Or results may include + * some sort of object detection components and frequency bounds etc could be used for + * classification. + * + * + */ +public interface DLPredictionDecision { + + /** + * Check whether a single prediction passes a binary classifier. Prediction which pass decision will be + * passed on to create new data units. + * @param result - the prediciton result to test. + * @return true if the result is passed. + */ + public boolean isBinaryResult(PredictionResult result); + +} diff --git a/src/rawDeepLearningClassifier/dlClassification/SimpleDLDecision.java b/src/rawDeepLearningClassifier/dlClassification/SimpleDLDecision.java new file mode 100644 index 00000000..248faf4f --- /dev/null +++ b/src/rawDeepLearningClassifier/dlClassification/SimpleDLDecision.java @@ -0,0 +1,49 @@ +package rawDeepLearningClassifier.dlClassification; + +import rawDeepLearningClassifier.dlClassification.animalSpot.StandardModelParams; + +/** + * Make a decision based on a simple binary threshold for a prediction. + */ +public class SimpleDLDecision implements DLPredictionDecision { + + /** + * Reference to the parameters. + */ + private StandardModelParams params; + + + @Override + public boolean isBinaryResult(PredictionResult modelResult) { + return isBinaryResult(modelResult, getParams()) ; + } + + + /** + * Check whether a model passes a binary test... + * @param modelResult - the model results + * @return the model results. + */ + private static boolean isBinaryResult(PredictionResult modelResult, StandardModelParams genericModelParams) { + for (int i=0; igenericModelParams.threshold && genericModelParams.binaryClassification[i]) { + // System.out.println("SoundSpotClassifier: prediciton: " + i + " passed threshold with val: " + modelResult.getPrediction()[i]); + return true; + } + } + return false; + } + + + public StandardModelParams getParams() { + return params; + } + + + public void setParams( StandardModelParams genericModelParams) { + this.params = genericModelParams; + } + +} diff --git a/src/rawDeepLearningClassifier/dlClassification/StandardClassifierModel.java b/src/rawDeepLearningClassifier/dlClassification/StandardClassifierModel.java index bc11e883..7d363263 100644 --- a/src/rawDeepLearningClassifier/dlClassification/StandardClassifierModel.java +++ b/src/rawDeepLearningClassifier/dlClassification/StandardClassifierModel.java @@ -44,6 +44,12 @@ public abstract class StandardClassifierModel implements DLClassiferModel, PamSe * in real time without slowing down the rest of PAMGaurd. */ private TaskThread workerThread; + + /** + * Makes a binary decision on whether a prediction result should go on + * to be part of a data unit. + */ + private SimpleDLDecision simpleDLDecision = new SimpleDLDecision(); public StandardClassifierModel(DLControl dlControl) { @@ -84,7 +90,7 @@ public abstract class StandardClassifierModel implements DLClassiferModel, PamSe for (int i =0; igenericModelParams.threshold && genericModelParams.binaryClassification[i]) { - // System.out.println("SoundSpotClassifier: prediciton: " + i + " passed threshold with val: " + modelResult.getPrediction()[i]); - return true; - } - } - return false; - } +// /** +// * Check whether a model passes a binary test... +// * @param modelResult - the model results +// * @return the model results. +// */ +// public static boolean isBinaryResult(StandardPrediction modelResult, StandardModelParams genericModelParams) { +// for (int i=0; igenericModelParams.threshold && genericModelParams.binaryClassification[i]) { +// // System.out.println("SoundSpotClassifier: prediciton: " + i + " passed threshold with val: " + modelResult.getPrediction()[i]); +// return true; +// } +// } +// return false; +// } @Override diff --git a/src/rawDeepLearningClassifier/layoutFX/.DS_Store b/src/rawDeepLearningClassifier/layoutFX/.DS_Store index d9c2dd0a..854e4d03 100644 Binary files a/src/rawDeepLearningClassifier/layoutFX/.DS_Store and b/src/rawDeepLearningClassifier/layoutFX/.DS_Store differ diff --git a/src/rawDeepLearningClassifier/logging/DLAnnotationType.java b/src/rawDeepLearningClassifier/logging/DLAnnotationType.java index 94c2c35e..170bf046 100644 --- a/src/rawDeepLearningClassifier/logging/DLAnnotationType.java +++ b/src/rawDeepLearningClassifier/logging/DLAnnotationType.java @@ -1,13 +1,14 @@ package rawDeepLearningClassifier.logging; import PamView.symbol.PamSymbolChooser; -import PamView.symbol.modifier.SymbolModifier; import annotation.CentralAnnotationsList; import annotation.DataAnnotationType; import annotation.binary.AnnotationBinaryHandler; +import annotation.dataselect.AnnotationDataSelCreator; import generalDatabase.SQLLoggingAddon; import rawDeepLearningClassifier.DLControl; import rawDeepLearningClassifier.dataPlotFX.DLSymbolModifier; +import rawDeepLearningClassifier.dataSelector.DLDataSelectCreator; /** * Annotation type for data from the matched click classifier. @@ -26,6 +27,8 @@ public class DLAnnotationType extends DataAnnotationType { private DLAnnotationSymbolChooser dlSymbolChooser; + private DLDataSelectCreator dlDataSelectorCreator; + public DLAnnotationType(DLControl mtControl) { this.dlControl=mtControl; dlAnnotationSQL = new DLAnnotationSQL(this); @@ -92,6 +95,15 @@ public class DLAnnotationType extends DataAnnotationType { public DLControl getDlControl() { return dlControl; } + + + @Override + public AnnotationDataSelCreator getDataSelectCreator(String selectorName, boolean allowScores) { + if (dlDataSelectorCreator == null) { + dlDataSelectorCreator = new DLDataSelectCreator(dlControl, this); + } + return dlDataSelectorCreator; + } diff --git a/src/rawDeepLearningClassifier/resources/deep_learning_data_selector.png b/src/rawDeepLearningClassifier/resources/deep_learning_data_selector.png new file mode 100644 index 00000000..20aaf4e7 Binary files /dev/null and b/src/rawDeepLearningClassifier/resources/deep_learning_data_selector.png differ diff --git a/src/rawDeepLearningClassifier/resources/deep_learning_symbol_manager.png b/src/rawDeepLearningClassifier/resources/deep_learning_symbol_manager.png new file mode 100644 index 00000000..a6c04e43 Binary files /dev/null and b/src/rawDeepLearningClassifier/resources/deep_learning_symbol_manager.png differ diff --git a/src/test/export/ExportTest.java b/src/test/export/ExportTest.java new file mode 100644 index 00000000..25678707 --- /dev/null +++ b/src/test/export/ExportTest.java @@ -0,0 +1,33 @@ +package test.export; + +import static org.junit.jupiter.api.Assertions.assertEquals; + + +import org.junit.jupiter.api.Test; + +import clickDetector.ClickDetection; + + +/** + * Tests for export functionality. + */ +public class ExportTest { + + + /** + * Test exporting detections to mat files. + */ + @Test + public void matFileTest() { + + System.out.println("Matched template classifier test: match corr"); + + //create a list of click detections. + ClickDetection clickDetection = new ClickDetection(0, 0, 0, null, null, 0); + + + //now open the mat file and check that we have all the data from these click detections. + + } + +} diff --git a/src/test/matchedTemplateClassifier/MatchedTemplateClassifierTest.java b/src/test/matchedTemplateClassifier/MatchedTemplateClassifierTest.java index cb4edc52..5825c599 100644 --- a/src/test/matchedTemplateClassifier/MatchedTemplateClassifierTest.java +++ b/src/test/matchedTemplateClassifier/MatchedTemplateClassifierTest.java @@ -83,8 +83,9 @@ public class MatchedTemplateClassifierTest { } /** - * Test the match correlation algorithm combined with click length algorithm. Here we awant to test that - * a long waveform + * Test the match correlation algorithm combined with click length algorithm. Here we want to test that + * a long waveform can be processed properly - i.e the peak of the click can be found and cross correlation performed + * on a shortened section. */ @Test public void testMatchCorrLen() { diff --git a/src/whistlesAndMoans/WhistleBinaryDataSource.java b/src/whistlesAndMoans/WhistleBinaryDataSource.java index eaa47a1a..bfe24f24 100644 --- a/src/whistlesAndMoans/WhistleBinaryDataSource.java +++ b/src/whistlesAndMoans/WhistleBinaryDataSource.java @@ -151,11 +151,15 @@ public class WhistleBinaryDataSource extends BinaryDataSource { /** * Bit of mess sorted out on 15/5/2020. Was working because module version went from 1 to 2 at same time - * as file version went from 3 to 4. May have been some middly stuff where file version and module + * as file version went from 3 to 4. May have been some middle stuff where file version and module * There is some FV 3 with MV 1, in which case data were probably duplicated. */ if (fileVersion > 3) { // basic data now in standard format. - firstSliceSample = startSample = binaryObjectData.getDataUnitBaseData().getStartSample(); + if (binaryObjectData.getDataUnitBaseData().getStartSample()==null) { + //some very rare circumstances + firstSliceSample =0; + } + else firstSliceSample = startSample = binaryObjectData.getDataUnitBaseData().getStartSample(); // if the DataUnitBaseData contains a sequence map, use it in place of the channel map if (binaryObjectData.getDataUnitBaseData().getSequenceBitmap()!=null) { diff --git a/src/whistlesAndMoans/dataSelector/WMDDataSelector.java b/src/whistlesAndMoans/dataSelector/WMDDataSelector.java index 62db9587..4b573de8 100644 --- a/src/whistlesAndMoans/dataSelector/WMDDataSelector.java +++ b/src/whistlesAndMoans/dataSelector/WMDDataSelector.java @@ -38,7 +38,7 @@ public class WMDDataSelector extends DataSelector { } @Override - public double scoreData(PamDataUnit pamDataUnit) { + public double scoreData(PamDataUnit pamDataUnit) { int combFlag = wmAlarmParameters.getCombinationFlag(); if (combFlag == DataSelectParams.DATA_SELECT_DISABLE) { return 1;