Merge pull request #86 from PAMGuard/main

Merge from main
This commit is contained in:
Douglas Gillespie 2024-07-22 13:50:49 +01:00 committed by GitHub
commit 3abaff0379
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
115 changed files with 4512 additions and 580 deletions

View File

@ -6,7 +6,7 @@
<attribute name="maven.pomderived" value="true"/>
</attributes>
</classpathentry>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-17">
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-21">
<attributes>
<attribute name="maven.pomderived" value="true"/>
</attributes>

1
.gitignore vendored
View File

@ -111,3 +111,4 @@ settings.xml
.classpath
.classpath
.settings/org.eclipse.jdt.core.prefs
.classpath

View File

@ -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/<project>=UTF-8
encoding/src=UTF-8

View File

@ -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

View File

@ -107,6 +107,7 @@
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
<exclude>test/resources/**</exclude>
</excludes>
</filter>
</filters>

Binary file not shown.

28
pom.xml
View File

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.pamguard</groupId>
<artifactId>Pamguard</artifactId>
<version>2.02.12</version>
<version>2.02.11d</version>
<name>Pamguard</name>
<description>Pamguard using Maven to control dependencies</description>
<url>www.pamguard.org</url>
@ -100,8 +100,8 @@
<excludes>
<exclude>META-INF/*.SF</exclude> <!-- get rid of manifests from library jars - also done in orig ant build file -->
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
<exclude>test/resources/**</exclude>
<exclude>META-INF/*.RSA</exclude>
<exclude>test/resources/**</exclude><!-- get rid of deep learning moduels and wav files used for unit tests -->
</excludes>
</filter>
</filters>
@ -180,25 +180,7 @@
-->
</executions>
</plugin>
<!-- Exclude the resource folder from build
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<version>3.2.0</version>
<configuration>
<resources>
<resource>
<directory>src/Resources</directory>
<excludes>
<exclude>src/test/resources/**</exclude>
</excludes>
</resource>
</resources>
</configuration>
</plugin>-->
</plugins>
@ -598,7 +580,7 @@
<dependency>
<groupId>com.fazecast</groupId>
<artifactId>jSerialComm</artifactId>
<version>2.11.0</version>
<version>2.5.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/edu.emory.mathcs/JTransforms -->

View File

@ -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.

View File

@ -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;
}

View File

@ -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<WavFileType> 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<PamAudioFileLoader> 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<PamAudioFileLoader> 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;
}
}

View File

@ -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<AcquisitionParameters>{
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<AcquisitionParameters>{
/**
*
*/
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<AcquisitionParameters>{
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<AcquisitionParameters>{
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<AcquisitionParameters>{
}
public PamBorderPane getAdvancedPane() {
return this.advancedSettingPane;
return this.flipPane.getAdvContentPane()
;
}
public Label getAdvancedLabel() {
return this.advLabel;
public TextField getAdvancedLabel() {
return this.flipPane.getAdvLabel();
}
}

View File

@ -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<FolderInputParameters>{
*/
private ToggleButton mergeContigious;
private PamBorderPane audioHolderloader;
// /**
// * The folder input system.
// * @param folderInputSystem - the folder system.
@ -245,8 +250,11 @@ public class FolderInputPane extends DAQSettingsPane<FolderInputParameters>{
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<FolderInputParameters>{
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<FolderInputParameters>{
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<FolderInputParameters>{
// fileDateStrip.setDate(folderInputSystem.getFileStartTime(selFiles[0]));
// }
}
/**
* Set bespoke options for certain file types.
*/
public void setFileOptionPane(ObservableList<WavFileType> 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<PamAudioFileLoader> 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<FolderInputParameters>{
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<PamAudioFileLoader> 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;
}
}

View File

@ -178,7 +178,7 @@ public class OfflineWavFileServer extends OfflineFileServer<FileDataMapPoint> {
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());

View File

@ -233,4 +233,10 @@ public class FlacAudioFile implements PamAudioFileLoader {
return (end.equals(".flac"));
}
@Override
public PamAudioSettingsPane getSettingsPane() {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -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();
}

View File

@ -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.
* <p>
@ -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<PamAudioFileLoader> 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<PamAudioFileLoader> getAudioFileLoaders(List<? extends File> files) {
ArrayList<PamAudioFileLoader> audioLoaders = new ArrayList<PamAudioFileLoader>();
PamAudioFileLoader loader;
for (int i=0; i<files.size(); i++) {
loader = getAudioFileLoader(files.get(i));
if (!audioLoaders.contains(loader)) {
audioLoaders.add(loader);
}
}
return audioLoaders;
}
/**
* Get the default file loader for raw files.

View File

@ -0,0 +1,34 @@
package Acquisition.pamAudio;
import PamView.panel.PamPanel;
import javafx.scene.layout.Pane;
/**
* User controls to change bespoke settings for audio loaders.
*/
public interface PamAudioSettingsPane {
/**
* Get the JavaFX pane for loading audio.
* @return
*/
public Pane getAudioLoaderPane();
/**
* Get the swing audio loader pane.
* @return
*/
public PamPanel getAudioLoaderPanel();
/**
* Get the parameters. This called whenever the settings dialog or pane is closed.
*/
public void getParams();
/**
* Set parameters. This is called when the dialog or pane is opened.
*/
public void setParams();
}

View File

@ -10,6 +10,8 @@ import javax.sound.sampled.UnsupportedAudioFileException;
//import org.kc7bfi.jflac.sound.spi.FlacAudioFileReader;
import org.jflac.sound.spi.FlacAudioFileReader;
import Acquisition.sud.SudAudioFileReader;
/**
* Now replaced with PamAudioFileManager.
*

View File

@ -296,5 +296,11 @@ public class WavAudioFile implements PamAudioFileLoader {
}
}
@Override
public PamAudioSettingsPane getSettingsPane() {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -0,0 +1,33 @@
package Acquisition.sud;
import java.io.Serializable;
/**
* Parameters for sud file extraction.
*/
public class PamSudParams implements Serializable, Cloneable {
public static final long serialVersionUID = 1L;
/**
* 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.
*/
public boolean zeroPad = true;
@Override
public PamSudParams clone() {
try {
PamSudParams ap = (PamSudParams) super.clone();
return ap;
}
catch (CloneNotSupportedException Ex) {
Ex.printStackTrace();
}
return null;
}
}

View File

@ -1,7 +1,8 @@
package Acquisition.pamAudio;
package Acquisition.sud;
import java.io.File;
import java.io.IOException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Arrays;
@ -12,7 +13,13 @@ import javax.swing.SwingUtilities;
import org.pamguard.x3.sud.ChunkHeader;
import org.pamguard.x3.sud.SudMapListener;
import Acquisition.AcquisitionParameters;
import Acquisition.pamAudio.PamAudioSettingsPane;
import Acquisition.pamAudio.WavAudioFile;
import PamController.PamControlledUnitSettings;
import PamController.PamController;
import PamController.PamSettingManager;
import PamController.PamSettings;
import PamUtils.worker.PamWorkProgressMessage;
import PamUtils.worker.PamWorkWrapper;
import PamUtils.worker.PamWorker;
@ -30,16 +37,32 @@ import PamUtils.worker.PamWorker;
* @author Jamie Macaulay
*
*/
public class SudAudioFile extends WavAudioFile {
public class SudAudioFile extends WavAudioFile implements PamSettings {
private Object conditionSync = new Object();
private volatile PamWorker<AudioInputStream> 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<String>(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;
}
}

View File

@ -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;
}
/**

View File

@ -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());
}
}

View File

@ -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; i<arr.length; i++) {
for(int j=0; j<arr.length; j++) {
cur = arr[i][j];
if (cur>max) {
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.

View File

@ -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);
}
}

View File

@ -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();

View File

@ -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
*
*/

View File

@ -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

View File

@ -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();
}
}

View File

@ -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

View File

@ -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<ChoiceTreeNode> choiceNodes;
private ArrayList<TreeNode> 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<<PamUtils.getNthChannel(i, modBits), i));
choiceNodes.add(new ChoiceTreeNode(this, 1<<PamUtils.getNthChannel(i, modBits), leafIndex++));
}
PamDialogPanel optionsPanel = modifier.getDialogPanel();
if (optionsPanel != null) {
choiceNodes.add(new OptionsTreeNode(this, modifier, optionsPanel, leafIndex++));
}
}
@Override
public TreeNode getChildAt(int childIndex) {
return choiceNodes.get(childIndex);
// return
// return
}
@Override
@ -73,16 +82,22 @@ public class ModifierTreeNode implements TreeNode {
}
public void setModBitmap(int modBitMap) {
for (ChoiceTreeNode cN : choiceNodes) {
cN.checkBox.setSelected((cN.selectionBit & modBitMap) != 0);
for (TreeNode tN : choiceNodes) {
if (tN instanceof ChoiceTreeNode) {
ChoiceTreeNode cN = (ChoiceTreeNode) tN;
cN.checkBox.setSelected((cN.selectionBit & modBitMap) != 0);
}
}
}
public int getModBitmap() {
int mp = 0;
for (ChoiceTreeNode cN : choiceNodes) {
if (cN.checkBox.isSelected()) {
mp |= cN.selectionBit;
for (TreeNode tN : choiceNodes) {
if (tN instanceof ChoiceTreeNode) {
ChoiceTreeNode cN = (ChoiceTreeNode) tN;
if (cN.checkBox.isSelected()) {
mp |= cN.selectionBit;
}
}
}
return mp;

View File

@ -0,0 +1,81 @@
package PamView.symbol.modifier.swing;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.Enumeration;
import javax.swing.JButton;
import javax.swing.tree.TreeNode;
import PamView.dialog.GenericSwingDialog;
import PamView.dialog.PamDialogPanel;
import PamView.symbol.modifier.SymbolModifier;
public class OptionsTreeNode implements TreeNode {
private ModifierTreeNode parent;
private SymbolModifier modifier;
private PamDialogPanel optionsPanel;
protected JButton optionsButton;
private int leafIndex;
public OptionsTreeNode(ModifierTreeNode parent, SymbolModifier modifier, PamDialogPanel optionsPanel, int leafIndex) {
this.parent = parent;
this.modifier = modifier;
this.optionsPanel = optionsPanel;
this.leafIndex = leafIndex;
optionsButton = new JButton("more ...");
optionsButton.setToolTipText("More symbol options");
optionsButton.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
showOptions();
}
});
}
private void showOptions() {
optionsPanel.setParams();
boolean ok = GenericSwingDialog.showDialog(null, "Options", optionsPanel);
if (ok) {
optionsPanel.getParams();
}
}
@Override
public TreeNode getChildAt(int childIndex) {
return null;
}
@Override
public int getChildCount() {
return 0;
}
@Override
public TreeNode getParent() {
return parent;
}
@Override
public int getIndex(TreeNode node) {
return leafIndex;
}
@Override
public boolean getAllowsChildren() {
return false;
}
@Override
public boolean isLeaf() {
return true;
}
@Override
public Enumeration<? extends TreeNode> children() {
return null;
}
}

View File

@ -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) {

View File

@ -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) {

View File

@ -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];
}

View File

@ -13,6 +13,8 @@ public class CompoundDataSelector extends DataSelector {
private CompoundParams compoundParams = new CompoundParams();
private CompoundDialogPaneFX compoundPaneFX;
public CompoundDataSelector(PamDataBlock pamDataBlock, ArrayList<DataSelector> allSelectors,
String selectorName, boolean allowScores, String selectorType) {
super(pamDataBlock, selectorName, allowScores);
@ -57,8 +59,10 @@ public class CompoundDataSelector extends DataSelector {
@Override
public DynamicSettingsPane<Boolean> 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;
}
}

View File

@ -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<Boolean> {
private CompoundDataSelector compoundDataSelector;
private ArrayList<DataSelector> selectorList;
private ArrayList<DataSelectorDialogPaneFX> 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<DataSelectorDialogPaneFX>(selectorList.size());
int ind = 0;
for (DataSelector ds : selectorList) {
DynamicSettingsPane<Boolean> 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
}
}

View File

@ -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;

View File

@ -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();

View File

@ -97,6 +97,23 @@ public abstract class DataSelector {
*/
abstract public DynamicSettingsPane<Boolean> 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.

View File

@ -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<Boolean> {
private static final double PREF_TOGGLE_WIDTH = 60;
private DataSelector dataSelector;
private DynamicSettingsPane<Boolean> 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<Boolean> 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
}
}

View File

@ -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;
}

View File

@ -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);
}

View File

@ -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<y2; i++){
g2d.drawLine(0,i,x1, i);
}
//color left of the thumb
g2d.setColor(new Color((int) colourArray[colourArray.length-1][0],(int)colourArray[colourArray.length-1][1],(int) colourArray[colourArray.length-1][2]));
for (int i=y1; i<y2; i++){
g2d.drawLine(x2,i, trackRect.width + thumbRect.width/2, i);
}
}
/**
* 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.

View File

@ -29,6 +29,7 @@ import PamView.PamSymbolType;
import PamView.dialog.PamDialog;
import PamView.dialog.PamDialogPanel;
import PamView.dialog.PamGridBagContraints;
import clickDetector.dataSelector.ClickDataSelector;
import pamMaths.HistogramDisplay;
import pamMaths.HistogramGraphicsLayer;
import pamMaths.PamHistogram;
@ -59,6 +60,7 @@ public class BTAmplitudeSelector implements PamDialogPanel {
private JCheckBox amplitudeSelect;
private JTextField minAmplitude;
private JFrame ownerFrame;
private ClickDataSelector dataSelector;
/**
* @param btDisplay
*/
@ -66,6 +68,7 @@ public class BTAmplitudeSelector implements PamDialogPanel {
super();
this.clickControl = clickControl;
this.btDisplay = btDisplay;
dataSelector = btDisplay.getClickDataSelector();
clickDataBlock = clickControl.getClickDataBlock();
histoPlot = new HistogramDisplay();
histoPlot.setGraphicsOverLayer(histoOverLayer = new HistoOverLayer());
@ -125,7 +128,8 @@ public class BTAmplitudeSelector implements PamDialogPanel {
axMin = allHistogram.getScaleMinVal();
axMax = allHistogram.getScaleMaxVal();
plotRectangle = g.getClipBounds();
double dx = (btDisplayParameters.minAmplitude - axMin) / (axMax-axMin) * plotRectangle.width;
// double dx = (btDisplayParameters.minAmplitude - axMin) / (axMax-axMin) * plotRectangle.width;
double dx = (dataSelector.getParams().minimumAmplitude - axMin) / (axMax-axMin) * plotRectangle.width;
int x = (int) Math.round(dx);
g.setColor(Color.RED);
g.drawLine(x, 0, x, plotRectangle.height);
@ -151,7 +155,7 @@ public class BTAmplitudeSelector implements PamDialogPanel {
double newAmp = (double) mouseEvent.getX() / histoOverLayer.plotRectangle.width *
(histoOverLayer.axMax-histoOverLayer.axMin) + histoOverLayer.axMin;
BTDisplayParameters btDisplayParameters = btDisplay.getBtDisplayParameters();
btDisplayParameters.minAmplitude = newAmp;
dataSelector.getParams().minimumAmplitude = newAmp;
ampCtrlPanel.setParams(btDisplayParameters);
redrawHisto();
//
@ -173,7 +177,7 @@ public class BTAmplitudeSelector implements PamDialogPanel {
if (btDisplayParameters.amplitudeSelect == false) {
return false;
}
double dx = (btDisplayParameters.minAmplitude - histoOverLayer.axMin) /
double dx = (dataSelector.getParams().minimumAmplitude - histoOverLayer.axMin) /
(histoOverLayer.axMax-histoOverLayer.axMin) * histoOverLayer.plotRectangle.width;
int x = (int) Math.round(dx);
if (Math.abs(e.getX()-x) > 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;

View File

@ -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;
}

View File

@ -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<ClickDetection> 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<JMenuItem> colOptList = getSymbolChooser().getQuickMenuItems(clickControl.getGuiFrame(), this, "Colour by ", SymbolModType.EVERYTHING, true);
ArrayList<JMenuItem> 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<ClickDetection> clickCopy = clickData.getDataCopy(displayStartMillis, displayStartMillis+displayLengthMillis, true, getDataSelector());
if (clickCopy.size() == 0) {
return;
}
ListIterator<ClickDetection> clickIterator = clickCopy.listIterator(clickCopy.size()-1);
ListIterator<ClickDetection> clickIterator = clickData.getListIterator(PamDataBlock.ITERATOR_END);
// synchronized (clickData.getSynchLock()) {
//
// ListIterator<ClickDetection> 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<ClickDetection> clickData = clickControl.getClickDataBlock();
ClickDetection click;
int n = 0;
double minAmpli = getClickDataSelector().getParams().minimumAmplitude;
synchronized (clickData.getSynchLock()) {
ListIterator<ClickDetection> 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);
}
}

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -95,7 +95,7 @@ public class ClickSelectPaneFX extends DynamicSettingsPane<Boolean> {
@Override
public String getName() {
return "Click Type Selection";
return "Filter by click type";
}
@Override

View File

@ -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<String> 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());
}

View File

@ -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<String> 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 {

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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();

View File

@ -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;
}

View File

@ -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<Boolean> 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<Double>("Min", "Max" , "", 2, 100, 1);
minMaxWidthPane = new DualControlField<Double>("", "" , "", 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<Double>("Min", "Max" , "", 2, 100, 1);
minMaxHeightPane = new DualControlField<Double>("", "" , "", 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.

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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.

View File

@ -44,7 +44,7 @@ public class RExportOverlayMenu extends ExportOverlayMenu {
* Create the export overlay
*/
public RExportOverlayMenu(){
rExportManger= new RExportManager();
rExportManger= new RExportManager(null);
buttonNode = createButton();

View File

@ -74,7 +74,7 @@ public abstract class SpectrumPlot <D extends PamDataUnit> implements Detection
private SpectrumSettingsPane spectrumSettingsPane;
private double sR;
private double storedsR;
// //TODO
@ -132,7 +132,7 @@ public abstract class SpectrumPlot <D extends PamDataUnit> implements Detection
if (data ==null) return;
this.sR=sR;
this.storedsR=sR;
int[] minmax = getAxisMinMaxSamples(plotProjector);
@ -172,7 +172,7 @@ public abstract class SpectrumPlot <D extends PamDataUnit> implements Detection
}
private double getSampleRate() {
return sR;
return storedsR;
}
@Override
@ -234,6 +234,8 @@ public abstract class SpectrumPlot <D extends PamDataUnit> 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);

View File

@ -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;
}
}

View File

@ -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;
}

View File

@ -82,7 +82,7 @@ public class MLWhistleMoanExport extends MLDataUnitExport<ConnectedRegionDataUni
// Struct mlStructure= new MLStructure("sliceData", new int[]{dataUnit.getConnectedRegion().getSliceData().size(), 1});
Struct mlStructure= Mat5.newStruct();
Struct mlStructure= Mat5.newStruct(dataUnit.getConnectedRegion().getSliceData().size(), 1);
//the start sample.
Matrix sliceNumber;

View File

@ -41,15 +41,21 @@ public interface PamDataUnitExporter {
public String getIconString();
/**
* Get the name of the exporter
* @return
* Get the name of the exporter.
* @return the name of the exporter.
*/
public String getName();
/**
* Clsoe the exporter
* Close the exporter.
*/
public void close();
/**
* Check whether and exporter needs a new file
* @return true if we need a new file.
*/
public boolean isNeedsNewFile();
}

View File

@ -56,11 +56,12 @@ public class PamExporterManager {
"yyyy_MM_dd_HHmmss");
public PamExporterManager() {
pamExporters = new ArrayList<PamDataUnitExporter>();
//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();
}
/**

View File

@ -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<RDataUnitExport> rDataExport = new ArrayList<RDataUnitExport>();
@ -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;
}
}

View File

@ -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<exportTaskGroup.getNTasks()) {
exportTaskGroup.runTaskFrom(taskIndex+1);
started = true;
}
}
}
}
/**
* Export task
*/
class ExportTaskGroup extends OfflineTaskGroup {
public ExportTaskGroup(String settingsName) {
super(null, settingsName);
}
@Override
public String getUnitType() {
return "Export Data";
}
/**
* Runs tasks from a specific task number.
* @param i - the index
*/
public void runTaskFrom(int i) {
System.out.println("RUN TASK FROM :" + i);
this.setPrimaryDataBlock(getTask(i).getDataBlock());
if (i<getNTasks()-1) {
//will start a new thread after this one has finished
this.setTaskMonitor(new ExportTaskMonitor(i, this));
}
super.runTasks();
}
/**
* Override the tasks o it runs through all tasks for each datablock. Usually
* task groups deal with just one parent datablock but exporters export from
* different data blocks. The only way to deal with this is to let the task run
* again and again through all tasks and letting tasks themselves check the
* correct data units are being exported.
*/
@Override
public boolean runTasks() {
runTaskFrom(0) ;
return true;
}
}

View File

@ -4,6 +4,7 @@ import PamController.PamController;
import PamguardMVC.PamDataBlock;
import PamguardMVC.PamDataUnit;
import PamguardMVC.dataSelector.DataSelectDialog;
import PamguardMVC.dataSelector.DataSelectParams;
import PamguardMVC.dataSelector.DataSelector;
import dataMap.OfflineDataMapPoint;
import export.PamExporterManager;
@ -16,12 +17,12 @@ import offlineProcessing.OfflineTask;
*
*/
public class ExportTask extends OfflineTask<PamDataUnit<?,?>>{
/**
* 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<PamDataUnit<?,?>>{
public ExportTask(PamDataBlock<PamDataUnit<?, ?>> 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<PamDataUnit<?,?>>{
@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<PamDataUnit<?,?>>{
@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<PamDataUnit<?,?>>{
* @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<PamDataUnit<?,?>>{
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;
}

View File

@ -465,6 +465,13 @@ public class WavFileExportManager implements PamDataUnitExporter {
@Override
public boolean isNeedsNewFile() {
return false;
}
// hello(){

View File

@ -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;
}

View File

@ -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) {

View File

@ -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;
}

View File

@ -142,7 +142,7 @@ public class OfflineTaskGroup implements PamSettings {
private ArrayList<OfflineTask> offlineTasks = new ArrayList<OfflineTask>();
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<Integer, TaskMonitorData> implements ViewLoadObserver {
public class TaskGroupWorker extends SwingWorker<Integer, TaskMonitorData> 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<long[]> timeChunks){
@ -976,4 +977,6 @@ public class OfflineTaskGroup implements PamSettings {
offlineTasks.clear();
}
}

View File

@ -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;

View File

@ -253,6 +253,10 @@ public class PamGuiTabFX extends PamTabFX {
double r1 = 1 - r;
int smallWindows = 0;
//the padding between windows
double padding = 10;
ArrayList<PamGuiInternalPane> 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;
}

View File

@ -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;
}

View File

@ -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<MouseEvent>() {
@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? true : false);
@ -363,6 +375,7 @@ public class HidingPane extends StackPane {
if (distance<expandedSize/2) showHidePane(overlay? true : false);
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<MouseEvent>() {
@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<MouseEvent>() {
@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<MouseEvent>() {
@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<MouseEvent>() {
// @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<MouseEvent>() {
// @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<MouseEvent>() {
// @Override public void handle(MouseEvent mouseEvent) {
@ -415,15 +432,15 @@ public class HidingPane extends StackPane {
// }
// });
pamButton.addEventHandler(ActionEvent.ACTION,new EventHandler<ActionEvent>() {
@Override
public void handle(ActionEvent e) {
System.out.println("HidingPane.showButton - action event clicked");
showHidePane(show);
}
});
// pamButton.addEventHandler(ActionEvent.ACTION,new EventHandler<ActionEvent>() {
// @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<ActionEvent>{
@Override
public void handle(ActionEvent arg0) {
showHidePane(false);
}
}
// /**
// * Called whenever the pin button is pressed.
// * @author Jamie Macaulay
// */
// class HideButtonPressed implements EventHandler<ActionEvent>{
//
// @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.

View File

@ -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();

View File

@ -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);
}
}

View File

@ -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<Number> 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<Number> 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();

View File

@ -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) {

View File

@ -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;

View File

@ -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 {

View File

@ -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;

View File

@ -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<num) {
classColors = new int[32];
}
//run through default colours
for (int i=0; i<classColors.length; i++) {
switch (i%8) {
case 0:
classColors[i]=PamUtilsFX.colorToInt(Color.RED);
break;
case 1:
classColors[i]=PamUtilsFX.colorToInt(Color.GREEN);
break;
case 2:
classColors[i]=PamUtilsFX.colorToInt(Color.BLUE);
break;
case 3:
classColors[i]=PamUtilsFX.colorToInt(Color.CYAN);
break;
case 4:
classColors[i]=PamUtilsFX.colorToInt(Color.MAGENTA);
break;
case 5:
classColors[i]=PamUtilsFX.colorToInt(Color.YELLOW);
break;
case 6:
classColors[i]=PamUtilsFX.colorToInt(Color.ORANGE);
break;
case 7:
classColors[i]=PamUtilsFX.colorToInt(Color.PURPLE);
break;
}
}
}
@Override
protected DLSymbolModifierParams clone() {

View File

@ -1,30 +1,43 @@
package rawDeepLearningClassifier.dataPlotFX;
import org.controlsfx.control.SegmentedButton;
import PamView.symbol.StandardSymbolOptions;
import PamView.symbol.modifier.SymbolModifier;
import PamView.symbol.modifier.SymbolModifierParams;
import javafx.geometry.Insets;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.control.CheckBox;
import javafx.scene.control.ColorPicker;
import javafx.scene.control.ComboBox;
import javafx.scene.control.Label;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Pane;
import javafx.scene.layout.Priority;
import pamViewFX.fxNodes.PamBorderPane;
import pamViewFX.fxNodes.PamHBox;
import pamViewFX.fxNodes.PamVBox;
import pamViewFX.fxNodes.comboBox.ColorComboBox;
import pamViewFX.fxNodes.sliders.ColourRangeSlider;
import pamViewFX.fxNodes.utilsFX.ColourArray;
import pamViewFX.fxNodes.utilsFX.PamUtilsFX;
import pamViewFX.symbol.StandardSymbolModifierPane;
import rawDeepLearningClassifier.dlClassification.DLClassName;
/*
* Symbol Options for the annotation pane
* Symbol Options for the annotation pane.
*
* @author Jamie Macaulay
*/
public class DLSymbolOptionPane extends StandardSymbolModifierPane {
private static final double CLASS_NAME_BOX_WIDTH = 130;
/**
* The colour range slider for colouring probabilities.
*/
@ -42,20 +55,142 @@ public class DLSymbolOptionPane extends StandardSymbolModifierPane {
/**
* Combo box which allows a user to select colour
* Color picker which allows a user to select the gradient for colouring predictions
*/
private ColorComboBox colourBox;
/**
* Color picker which allows a user to select colour for each class.
*/
private ColorPicker colourPicker;
private boolean initialised=true;
private DLSymbolModifier dlSymbolModifier;
private DLSymbolModifier dlSymbolModifier;
private ComboBox<String> 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<String>();
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<String> 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;
}

View File

@ -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<String> classNameBox;
private JComboBox<String> 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<String> classNameBox) {
DLClassName[] classNames = dlSymbolModifier.getDLAnnotType().getDlControl().getDLModel().getClassNames();
// for (int i =0; i<classNames.length; i++) {
// System.out.println("DLSymbolOptionsPane: classNames: " + i + " " + classNames[i].className);
// }
int nClass = dlSymbolModifier.getDLAnnotType().getDlControl().getDLModel().getNumClasses();
classNameBox.removeAllItems();
for (int i=0; i<nClass; i++) {
if (classNames!=null && classNames.length>i) {
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<String>();
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();
}
}

View File

@ -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<DataSelectParams> getSettingsPane();
/**
* Swing settings controls for the data deep learning filter.
* @return Swing dialog for the settings
*/
public PamDialogPanel getSettingsPanel();
}

View File

@ -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<DLAnnotation> {
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<DLAnnotation> dataAnnotationType, String selectorName,
boolean allowScores, String selectorType) {
// TODO Auto-generated method stub
return new DLDataSelector(dlcontrol, dataAnnotationType, null,
selectorName, allowScores);
}
}

View File

@ -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.
* <p>
* The data selector can have different types of data selectors which can
* depend on the classifier used and user choice.
* <p>
* 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<DLAnnotation> {
/**
* Swing panel for the data selector.
*/
private DLSelectPanel dlSelectPanel;
/**
* FX panel for the data selector.
*/
private DLSelectPaneFX dlSelectPaneFX;
/**
* Data filter for filtering.
*/
private List<DLDataFilter> dataFilters = new ArrayList<DLDataFilter> ();
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<DLAnnotation> 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<dataFilters.size() ; i++) {
dlDataSelectParams.dataSelectorParams[i] = dataFilters.get(i).getParams();
}
}
@Override
public PamDialogPanel getDialogPanel() {
if (dlSelectPanel == null) {
dlSelectPanel = new DLSelectPanel(this);
}
return dlSelectPanel;
}
@Override
public DynamicSettingsPane<Boolean> 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<dlDataSelectParams.dataSelectorParams.length; i++) {
dataFilters.get(i).setParams((DataSelectParams) dlDataSelectParams.dataSelectorParams[i]);
}
}
}
/**
* Check that the data filters are the corret size.
* @param dataSelectParams - the apramters to set.
*/
private void checkDataFilterParamsSize(DLDataSelectorParams dataSelectParams) {
if (dataSelectParams.dataSelectorParams==null || dataSelectParams.dataSelectorParams.length!=dataFilters.size()) {
dataSelectParams.dataSelectorParams = new DataSelectParams[dataFilters.size()];
for (int i=0; i<dataSelectParams.dataSelectorParams.length; i++) {
dataSelectParams.dataSelectorParams[i] = dataFilters.get(i).getParams();
}
}
}
@Override
public DLDataSelectorParams getParams() {
//get the paramters from the pane.
for (int i=0; i<dlDataSelectParams.dataSelectorParams.length; i++) {
dlDataSelectParams.dataSelectorParams[i] = dataFilters.get(i).getParams();
}
return dlDataSelectParams;
}
@Override
public double scoreData(PamDataUnit pamDataUnit) {
int score = dataFilters.get(dlDataSelectParams.dataSelectorIndex).scoreDLData(pamDataUnit);
return score>=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<DLDataFilter> getDataSelectors() {
return dataFilters;
}
}

View File

@ -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;
}

View File

@ -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; j<results.length; j++) {
if (filterParams.classSelect[j] && results[j]>filterParams.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<filterParams.classSelect.length; i++) {
filterParams.classSelect[i]=true;
filterParams.minClassPredicton[i]=0.4;
}
}
}
@Override
public DynamicSettingsPane getSettingsPane() {
if (dlPredictonPane ==null) {
dlPredictonPane = new DLPredictonPane(this);
}
return dlPredictonPane;
}
@Override
public PamDialogPanel getSettingsPanel() {
if (dlPredictonPanel ==null) {
dlPredictonPanel = new DLPredictionPanel(this);
}
return dlPredictonPanel;
}
public DLControl getDLControl() {
return this.dlcontrol;
}
// /**
// * Get the index of the highest prediciton value a list of results.
// * @param predictions - index of the highest prediction within a matrix of predicitons.
// * @return an index of the hghest predictions.
// */
// public static int[] getBestClass(List<PredictionResult> 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;
// }
}

View File

@ -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;
}
}

View File

@ -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<params.classSelect.length ; i++) {
//set the correct params
enableClass[i].setSelected(params.classSelect[i]);
spinnerClass[i].setValue(params.minClassPredicton[i]);
}
}
private void setClassPane(DLPredictionFilterParams input) {
DLClassName[] classNames = predicitonFilter.getDLControl().getDLModel().getClassNames();
contentPanel.removeAll();
enableClass = new JCheckBox[input.classSelect.length];
spinnerClass = new JSpinner[input.classSelect.length];
GridBagConstraints c = new PamGridBagContraints();
c.ipadx =5;
c.gridwidth=2;
contentPanel.add(new Label("Min. prediciton for each class"), c);
c.gridwidth=1;
c.gridy++;
for (int i=0; i<input.classSelect.length ; i++) {
//create the row
c.gridx = 0;
enableClass[i] = new JCheckBox(classNames[i].className);
final int ii = i;
enableClass[i].addActionListener((action)->{
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; j<spinnerClass.length ; j++) {
if (j!=ii) {
spinnerClass[j].setValue(spinnerClass[ii].getValue());
}
}
}
}
});
spinnerClass[i].setToolTipText(classNames[i].className);
contentPanel.add(spinnerClass[i], c);
if (i==0 && input.classSelect.length>1) {
//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<spinnerClass.length ; i++) {
currParams.classSelect[i] = enableClass[i].isSelected();
currParams.minClassPredicton[i] = (double) spinnerClass[i].getValue();
}
predicitonFilter.setParams(currParams);
return true;
}
}

View File

@ -0,0 +1,207 @@
package rawDeepLearningClassifier.dataSelector;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.CheckBox;
import javafx.scene.control.Label;
import javafx.scene.control.Slider;
import javafx.scene.control.ToggleButton;
import javafx.scene.control.Tooltip;
import javafx.scene.layout.Priority;
import pamViewFX.PamGuiManagerFX;
import pamViewFX.fxGlyphs.PamGlyphDude;
import pamViewFX.fxNodes.PamBorderPane;
import pamViewFX.fxNodes.PamHBox;
import pamViewFX.fxNodes.PamVBox;
import pamViewFX.fxSettingsPanes.DynamicSettingsPane;
import rawDeepLearningClassifier.dlClassification.DLClassName;
/**
* Settings pane for filtering deep learning results by class prediciton.
*/
public class DLPredictonPane extends DynamicSettingsPane<DLPredictionFilterParams>{
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<classPanes.length; i++) {
if (classPanes[i].enable.isSelected() && i!=index) {
classPanes[i].enableListener = false; //prevent needless calls to notify settings
classPanes[i].slider.setValue(newval.doubleValue());
classPanes[i].enableListener = true;
}
};
}
notifySettingsListeners();
});
slider.setMin(0.);
slider.setMax(1.);
PamHBox.setHgrow(slider, Priority.ALWAYS);
valueLabel = new Label();
valueLabel.setMinWidth(30);
valueLabel.setText(String.format("%.2f", slider.getValue()));
this.getChildren().addAll(enable, slider, valueLabel);
}
}
@Override
public DLPredictionFilterParams getParams(DLPredictionFilterParams currParams) {
for (int i=0; i<classPanes.length ; i++) {
currParams.classSelect[i] = classPanes[i].enable.isSelected();
currParams.minClassPredicton[i] = classPanes[i].slider.getValue();
}
return currParams;
}
@Override
public void setParams(DLPredictionFilterParams input) {
//set the parameters. Note that class numbers should have been checked already.
setClassPane(input);
}
private void setClassPane(DLPredictionFilterParams input) {
DLClassName[] classNames = predicitonFilter.getDLControl().getDLModel().getClassNames();
contentPane.getChildren().clear();
classPanes = new ClassDataSelector[input.classSelect.length];
ClassDataSelector classPane;
for (int i=0; i<input.classSelect.length ; i++) {
classPane = new ClassDataSelector(classNames[i].className, i);
classPanes[i] = classPane;
contentPane.getChildren().add(classPane);
classPane.enable.setSelected(input.classSelect[i]);
classPane.slider.setValue(input.minClassPredicton[i]);
if (i==input.classSelect.length-1) {
classPane.slider.setShowTickLabels(true);
}
}
}
// /**
// * Check the class numbers are correct
// * @param input
// */
// private void checkSpeciesClassNumbers(DLPredictionFilterParams input) {
// this.predicitonFilter.getSpeciesClassList();
//
// }
@Override
public String getName() {
return "Deep learning prediciton filter";
}
@Override
public Node getContentNode() {
return mainPane;
}
@Override
public void paneInitialized() {
// TODO Auto-generated method stub
}
}

View File

@ -0,0 +1,102 @@
package rawDeepLearningClassifier.dataSelector;
import javafx.scene.Node;
import pamViewFX.fxNodes.PamBorderPane;
import pamViewFX.fxSettingsPanes.DynamicSettingsPane;
/**
* JavaFX pane for the deep learning data selector. This simply selects the rype
* of filter to use and sets that as the controls.
* <p>
* 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<Boolean>{
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<dlDataSelector.getDataSelectors().size(); i++) {
dlDataSelector.getDataSelectors().get(currentIndex).getSettingsPane().addSettingsListener(()->{
//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
}
}

View File

@ -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;
}
}
}

View File

@ -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.
<p align="center">
<img width="550" height="700" src = "resources/default_settings_humpback_1.png">
</p>
_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.
<p align="center">
<img width="429" height="150" src = "resources/deep_learning_symbol_manager.png">
</p>
_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.
<p align="center">
<img width="190" height="170" src = "resources/deep_learning_data_selector.png">
</p>
_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.

Some files were not shown because too many files have changed in this diff Show More