diff --git a/.classpath b/.classpath index 49faf461..6ad8f1d7 100644 --- a/.classpath +++ b/.classpath @@ -6,8 +6,9 @@ - + + diff --git a/.settings/org.eclipse.core.resources.prefs b/.settings/org.eclipse.core.resources.prefs index 51bb81c3..a474f512 100644 --- a/.settings/org.eclipse.core.resources.prefs +++ b/.settings/org.eclipse.core.resources.prefs @@ -1,5 +1,6 @@ eclipse.preferences.version=1 encoding//src/rawDeepLearningClassifer/segmenter/SegmenterProcess.java=UTF-8 +encoding//src/test=UTF-8 encoding//src/test/resources=UTF-8 encoding/=UTF-8 encoding/src=UTF-8 diff --git a/README.files/colorschememapping.xml b/README.files/colorschememapping.xml new file mode 100644 index 00000000..6a0069cd --- /dev/null +++ b/README.files/colorschememapping.xml @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/README.files/themedata.thmx b/README.files/themedata.thmx new file mode 100644 index 00000000..d641fac9 Binary files /dev/null and b/README.files/themedata.thmx differ diff --git a/src/Acquisition/FileInputSystem.java b/src/Acquisition/FileInputSystem.java index f3988d61..6fd76f3e 100644 --- a/src/Acquisition/FileInputSystem.java +++ b/src/Acquisition/FileInputSystem.java @@ -595,7 +595,7 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe System.out.println("The current file was null"); return false; } -// System.out.printf("*********************************** Opening file %s\n", currentFile.getName()); + System.out.printf("*********************************** Opening file %s\n", currentFile.getName()); try { @@ -639,6 +639,9 @@ public class FileInputSystem extends DaqSystem implements ActionListener, PamSe fileInputParameters.bitDepth = audioFormat.getSampleSizeInBits(); loadByteConverter(audioFormat); + +// System.out.println("FileInputSystem - prepareInputFile done"); + } catch (UnsupportedAudioFileException ex) { ex.printStackTrace(); diff --git a/src/Acquisition/FolderInputSystem.java b/src/Acquisition/FolderInputSystem.java index 912810b0..d3904fb4 100644 --- a/src/Acquisition/FolderInputSystem.java +++ b/src/Acquisition/FolderInputSystem.java @@ -933,6 +933,7 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D @Override public InputStoreInfo getStoreInfo(boolean detail) { + System.out.println("FolderInputSystem: Get store info start:"); if (allFiles == null || allFiles.size() == 0) { return null; } @@ -967,6 +968,7 @@ public class FolderInputSystem extends FileInputSystem implements PamSettings, D storeInfo.setFileStartTimes(allFileStarts); storeInfo.setFileEndTimes(allFileEnds); } + System.out.println("FolderInputSystem: Get store info complete:"); return storeInfo; } diff --git a/src/PamController/PamController.java b/src/PamController/PamController.java index 161e673b..1f8d340c 100644 --- a/src/PamController/PamController.java +++ b/src/PamController/PamController.java @@ -851,7 +851,7 @@ public class PamController implements PamControllerInterface, PamSettings { String help = null; int ans = WarnOnce.showWarning(title, msg, WarnOnce.WARNING_MESSAGE, help, e); System.err.println("Exception while loading " + moduleName); - this.removeControlledUnt(unitBeingLoaded); + //this.removeControlledUnt(unitBeingLoaded); this.clearLoadedUnit(); ; } diff --git a/src/PamController/PamSettingManager.java b/src/PamController/PamSettingManager.java index 43d92d09..49b0bfef 100644 --- a/src/PamController/PamSettingManager.java +++ b/src/PamController/PamSettingManager.java @@ -55,16 +55,6 @@ import pamguard.GlobalArguments; //import org.w3c.dom.Node; //import com.thoughtworks.xstream.XStream; - - - - - - - -import java.io.StringWriter; - - //import javax.xml.transform.OutputKeys; //import javax.xml.transform.Transformer; //import javax.xml.transform.TransformerException; @@ -73,13 +63,6 @@ import java.io.StringWriter; //import javax.xml.transform.stream.StreamResult; - - - - - - - //import sun.jdbc.odbc.OdbcDef; import tipOfTheDay.TipOfTheDayManager; //import javax.swing.filechooser.FileFilter; @@ -90,11 +73,7 @@ import PamController.settings.SettingsNameChanger; import PamUtils.PamCalendar; import PamUtils.PamFileChooser; import PamUtils.PamFileFilter; -import PamUtils.Splash; -import PamView.PamGui; import PamView.dialog.warn.WarnOnce; -import amplifier.AmpDialog; -import amplifier.AmpParameters; //import PamUtils.PamFileFilter; @@ -172,6 +151,7 @@ public class PamSettingManager { static public final String fileEnd = "psf"; static public final String fileEndx = "psfx"; static public final String fileEndXML = "psfxml"; + private static boolean saveAsPSFX = true; static public String getCurrentSettingsFileEnd() { @@ -220,11 +200,12 @@ public class PamSettingManager { /** * Save settings to a psf file */ - static private final int SAVE_PSF = 0x1; + static public final int SAVE_PSF = 0x1; + /** * Save settings to database tables (if available). */ - static private final int SAVE_DATABASE = 0x2; + static public final int SAVE_DATABASE = 0x2; /** * running in remote mode, default normal @@ -800,7 +781,6 @@ public class PamSettingManager { /* * then save it to a single XML file */ - //XML file test objectToXMLFile(pamSettingsList,file); diff --git a/src/PamController/fileprocessing/ReprocessManager.java b/src/PamController/fileprocessing/ReprocessManager.java index b4e8c395..2541187b 100644 --- a/src/PamController/fileprocessing/ReprocessManager.java +++ b/src/PamController/fileprocessing/ReprocessManager.java @@ -172,7 +172,7 @@ public class ReprocessManager { choiceSummary.addChoice(ReprocessStoreChoice.STARTNORMAL); return choiceSummary; } - + choiceSummary.addChoice(ReprocessStoreChoice.STARTNORMAL); ArrayList outputStores = PamController.getInstance().findControlledUnits(DataOutputStore.class, true); diff --git a/src/PamModel/PamModel.java b/src/PamModel/PamModel.java index 61242ea8..f68ede3a 100644 --- a/src/PamModel/PamModel.java +++ b/src/PamModel/PamModel.java @@ -24,7 +24,6 @@ import java.io.File; import java.io.FileNotFoundException; import java.io.Serializable; import java.lang.reflect.Constructor; -import java.lang.reflect.Method; import java.net.URL; import java.net.URLClassLoader; import java.util.ArrayList; @@ -52,8 +51,6 @@ import rockBlock.RockBlockControl; import tethys.TethysControl; import turbineops.TurbineOperationControl; import GPS.GpsDataUnit; -import Map.MapController; -import Map.gridbaselayer.GridbaseControl; import NMEA.NMEADataUnit; import PamController.PamControlledUnitSettings; import PamController.PamController; @@ -67,7 +64,6 @@ import PamguardMVC.PamDataBlock; import analogarraysensor.ArraySensorControl; import backupmanager.BackupManager; import beamformer.continuous.BeamFormerControl; -import beamformer.localiser.BeamFormLocaliserControl; import bearinglocaliser.BearingLocaliserControl; import binaryFileStorage.SecondaryBinaryStore; import cepstrum.CepstrumControl; diff --git a/src/PamUtils/PamArrayUtils.java b/src/PamUtils/PamArrayUtils.java index b6a4da62..ce4318a9 100644 --- a/src/PamUtils/PamArrayUtils.java +++ b/src/PamUtils/PamArrayUtils.java @@ -712,6 +712,34 @@ public class PamArrayUtils { // return normArr; } + + + /** + * Normalise an array + * @param arr - the array to normalise + * @param scaleFactor - multiply the resulting array by a scale factor. + * @return normalised copy of the array + */ + public static float[] normalise(float[] arr, double scaleFactor) { + // //first find the sum of the square of the wave + if (arr != null) { + int n = arr.length; + double sum = 0.0; + + for (int i = 0; i < n; i++) { + sum += arr[i] * arr[i]; + } + sum = Math.pow(sum, 0.5); + + float[] normArr=new float[arr.length]; + for (int i=0; i - - - - - - - - - - - delphinID - - - - - - diff --git a/src/Resources/screenshots/Click_detector_screenshot.png b/src/Resources/screenshots/Click_detector_screenshot.png new file mode 100644 index 00000000..987c65f2 Binary files /dev/null and b/src/Resources/screenshots/Click_detector_screenshot.png differ diff --git a/src/Resources/screenshots/Datamap_screenshot.png b/src/Resources/screenshots/Datamap_screenshot.png new file mode 100644 index 00000000..e43f4d72 Binary files /dev/null and b/src/Resources/screenshots/Datamap_screenshot.png differ diff --git a/src/dataMap/layoutFX/DataMapGUIFX.java b/src/dataMap/layoutFX/DataMapGUIFX.java index 29e7d521..40340995 100644 --- a/src/dataMap/layoutFX/DataMapGUIFX.java +++ b/src/dataMap/layoutFX/DataMapGUIFX.java @@ -27,6 +27,7 @@ public class DataMapGUIFX extends PamControlledGUIFX implements DataMapControlGU * The data map displays to add. */ public ArrayList getDisplays(){ + try { if (dataMapDisplays==null){ dataMapPaneFX = new DataMapPaneFX(dataMapControl); dataMapDisplays=new ArrayList(); @@ -34,6 +35,11 @@ public class DataMapGUIFX extends PamControlledGUIFX implements DataMapControlGU dataMapDisplays.add(dataMapPaneFX); } return dataMapDisplays; + } + catch (Exception e) { + e.printStackTrace(); + } + return null; } @Override diff --git a/src/dataMap/layoutFX/DataMapPaneFX.java b/src/dataMap/layoutFX/DataMapPaneFX.java index 82834eae..da97d4e4 100644 --- a/src/dataMap/layoutFX/DataMapPaneFX.java +++ b/src/dataMap/layoutFX/DataMapPaneFX.java @@ -55,7 +55,7 @@ public class DataMapPaneFX extends PamBorderPane implements UserDisplayNodeFX { /** * The hiding pane which holds the summary pane. */ - private HidingPane hidingSummaryPane; + private HidingPane hidingSettingsPane; /** * The buttons which shows the top hiding pane. @@ -65,7 +65,7 @@ public class DataMapPaneFX extends PamBorderPane implements UserDisplayNodeFX { /** * Pane which allows users to change scale on datamap. */ - private ScalePaneFX scalePane; + private DataMapSettingsPane dataMapSettingsPane; private PamVBox settingsPane; @@ -87,14 +87,14 @@ public class DataMapPaneFX extends PamBorderPane implements UserDisplayNodeFX { //create all the different panes, summaryPane = new SummaryPaneFX(dataMapControl, this); - scalePane=new ScalePaneFX(dataMapControl,this); - scrollingDataPanel= new ScrollingDataPaneFX(dataMapControl, this); + dataMapSettingsPane=new DataMapSettingsPane(dataMapControl,this); + //create the setting spane settingsPane=new PamVBox(); // settingsPane.getChildren().add(summaryPane); - settingsPane.getChildren().add(scalePane); + settingsPane.getChildren().add(dataMapSettingsPane.getContentNode()); settingsPane.setPadding(new Insets(40,10,10,10)); settingsPane.setPrefWidth(HIDE_PANE_WIDTH); @@ -105,16 +105,16 @@ public class DataMapPaneFX extends PamBorderPane implements UserDisplayNodeFX { //topHolder.prefHeightProperty().bind(summaryPane.prefHeightProperty()); //hiding summary pane - hidingSummaryPane=new HidingPane(Side.RIGHT, settingsPane, this, true); - hidingSummaryPane.setVisibleImmediatly(false); - hidingSummaryPane.showHidePane(true); - hidingSummaryPane.getStyleClass().add("pane-trans"); - hidingSummaryPane.getStylesheets().addAll(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS()); - StackPane.setAlignment(hidingSummaryPane, Pos.TOP_RIGHT); - hidingSummaryPane.setPrefWidth(HIDE_PANE_WIDTH); + hidingSettingsPane=new HidingPane(Side.RIGHT, settingsPane, this, true); + hidingSettingsPane.setVisibleImmediatly(false); + hidingSettingsPane.showHidePane(true); + hidingSettingsPane.getStyleClass().add("pane-trans"); + hidingSettingsPane.getStylesheets().addAll(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS()); + StackPane.setAlignment(hidingSettingsPane, Pos.TOP_RIGHT); + hidingSettingsPane.setPrefWidth(HIDE_PANE_WIDTH); //style the show button. - showButton=hidingSummaryPane.getShowButton(); + showButton=hidingSettingsPane.getShowButton(); showButton.getStyleClass().add("close-button-left"); showButton.getStylesheets().addAll(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getSlidingDialogCSS()); @@ -127,7 +127,7 @@ public class DataMapPaneFX extends PamBorderPane implements UserDisplayNodeFX { StackPane.setAlignment(showButton, Pos.CENTER_RIGHT); StackPane stackPane = new StackPane(); - stackPane.getChildren().addAll(scrollingDataPanel, hidingSummaryPane, showButton); + stackPane.getChildren().addAll(scrollingDataPanel, hidingSettingsPane, showButton); dateAxis = new PamDateAxis(); dateAxis.setMinHeight(50); @@ -179,11 +179,11 @@ public class DataMapPaneFX extends PamBorderPane implements UserDisplayNodeFX { * to do with scaling changes. */ public void scaleChanged() { - if (scalePane == null || scrollingDataPanel == null) { + if (dataMapSettingsPane == null || scrollingDataPanel == null) { return; } - scalePane.getParams(dataMapControl.dataMapParameters); - scrollingDataPanel.scaleChange(); + dataMapSettingsPane.getParams(dataMapControl.dataMapParameters); + //scrollingDataPanel.scaleChange(); } @Override @@ -227,24 +227,30 @@ public class DataMapPaneFX extends PamBorderPane implements UserDisplayNodeFX { switch (changeType) { case PamControllerInterface.INITIALIZATION_COMPLETE: scrollingDataPanel.updateScrollBar(); - scalePane.checkDataGramPane(); + dataMapSettingsPane.setParams(dataMapControl.dataMapParameters); this.repaintAll(); + break; case PamControllerInterface.CHANGED_OFFLINE_DATASTORE: scrollingDataPanel.updateScrollBar(); - scalePane.checkDataGramPane(); + dataMapSettingsPane.checkDataGramPane(); + dataMapSettingsPane.setParams(dataMapControl.dataMapParameters); + break; case PamControllerInterface.ADD_CONTROLLEDUNIT: - scalePane.checkDataGramPane(); case PamControllerInterface.REMOVE_CONTROLLEDUNIT: - scalePane.checkDataGramPane(); + dataMapSettingsPane.checkDataGramPane(); + dataMapSettingsPane.setParams(dataMapControl.dataMapParameters); + break; case PamControllerInterface.INITIALIZE_LOADDATA: case PamControllerInterface.EXTERNAL_DATA_IMPORTED: scrollingDataPanel.updateScrollBar(); - scalePane.checkDataGramPane(); + dataMapSettingsPane.checkDataGramPane(); + dataMapSettingsPane.setParams(dataMapControl.dataMapParameters); this.repaintAll(); break; case PamControllerInterface.OFFLINE_DATA_LOADED: scrollingDataPanel.updateScrollBar(); - scalePane.checkDataGramPane(); + dataMapSettingsPane.checkDataGramPane(); + dataMapSettingsPane.setParams(dataMapControl.dataMapParameters); this.repaintAll(); break; case PamControllerInterface.DATA_LOAD_COMPLETE: @@ -303,4 +309,22 @@ public class DataMapPaneFX extends PamBorderPane implements UserDisplayNodeFX { return null; } + /** + * Get the current number of data stream panes + * @return the number of data stream panes + */ + public int getNumDataStreamPanes() { + return this.scrollingDataPanel.getNumDataStreamPanes(); + } + + /** + * Get a data stream pane. + * @param n - the index of the data stream pane + * @return the data stream pane or null if the index is out of bounds. + */ + public DataStreamPaneFX getDataStreamPane(int n) { + return this.scrollingDataPanel.getDataSyreamPane( n); + + } + } diff --git a/src/dataMap/layoutFX/DataMapScrollBar.java b/src/dataMap/layoutFX/DataMapScrollBar.java new file mode 100644 index 00000000..902eef41 --- /dev/null +++ b/src/dataMap/layoutFX/DataMapScrollBar.java @@ -0,0 +1,101 @@ +package dataMap.layoutFX; + +import java.util.ArrayList; + +import PamController.OfflineDataStore; +import PamController.PamController; +import dataMap.DataMapControl; +import dataMap.OfflineDataMap; +import dataMap.OfflineDataMapPoint; +import javafx.scene.paint.Color; +import pamViewFX.fxNodes.pamScrollers.acousticScroller.ScrollBarPane; + +/** + * A scroll bar whihc shows a summary fo the data. + */ +public class DataMapScrollBar extends ScrollBarPane { + + private DataMapControl dataMapControl; + + public DataMapScrollBar(DataMapControl dataMapControl) { + super(); + this.dataMapControl=dataMapControl; + } + + /** + * Paint a summary of the data + */ + public void paintDataSummary() { + + ArrayList offlineDataStores = PamController.getInstance().findOfflineDataStores(); + OfflineDataStore aSource; + + Color color = Color.DODGERBLUE; + + this.getDrawCanvas().getGraphicsContext2D().setStroke(color); + this.getDrawCanvas().getGraphicsContext2D().setGlobalAlpha(0.3); + + for (int i = 0; i < offlineDataStores.size(); i++) { + aSource = offlineDataStores.get(i); + paintOfflineDataSource(aSource); + } + } + + /** + * Paint the data on the canvas. + * @param dataSource - the dta source. + */ + private void paintOfflineDataSource(OfflineDataStore dataSource) { + if (dataMapControl.getMappedDataBlocks()==null) return; + + OfflineDataMap aMap; + for (int i = 0; i < dataMapControl.getMappedDataBlocks().size(); i++) { + aMap = dataMapControl.getMappedDataBlocks().get(i).getOfflineDataMap(dataSource); + if (aMap == null) { + continue; + } + long lastTime; + OfflineDataMapPoint aPoint; + for (int j=0; j { + + /* + * Reference to the data map control. + */ + private DataMapControl dataMapControl; + + /** + * Reference to the dataMapPane. + */ + private DataMapPaneFX dataMapPane; + +// /** +// * The slider which determines time scale. +// */ +// private Slider timeSlider; + + /** + * Shows the time scale in pix/hour + */ + private Label timeScaleLabel; + + /** + * Selects unit count on vertical scale + */ + private ComboBox scaleBox; + + /** + * Check box for log vertical scale. + */ + private PamToggleSwitch logScaleToggle; + +// /** +// * The chosen time values. +// */ +// private double[] timeScaleChoices = DataMapParameters.hScaleChoices; + + /** + * Combo box holding options to channge the datargam bin size + */ + private ComboBox datagramBox; + + /** + * Holds a list of times for the datagram bin size. + */ + private ComboBox dataGramComboBox; + + /** + * Holds everything. + */ + private PamVBox holder; + + /** + * Grid pane with settings for the data scales + */ + private PamGridPane scaleSettingsPane; + + private Label dataGramLabel; + + private ComboBox dataGramBox; + + /** + * Holdes datagram settings. + */ + private PamVBox dataGramSettingsPane; + + private DataGramColPane dataGramColPane; + + private PamBorderPane mainPain; + + + + public DataMapSettingsPane(DataMapControl dataMapControl, DataMapPaneFX dataMapPane) { + super(null); + this.dataMapControl = dataMapControl; + this.dataMapPane = dataMapPane; + + // //create the holder pane. + // PamVBox vBoxHolder=new PamVBox(); + // vBoxHolder.setSpacing(5); + // Label title=new Label("Scales"); + // PamGuiManagerFX.titleFont2style(title); + // vBoxHolder.getChildren().addAll(title, controlPane); + + //add the scale + holder=new PamVBox(); + holder.setSpacing(5); + + Label scaleLabel = new Label("Data Scale"); + PamGuiManagerFX.titleFont2style(scaleLabel); + holder.getChildren().add(scaleLabel); + holder.getChildren().add(scaleSettingsPane = createScalePane()); + + //adds the datagram settings pane which is dependent on binary storage. + checkDataGramPane(); + + mainPain = new PamBorderPane(); + mainPain.setCenter(holder); + + //set params for the pane + setParams(dataMapControl.dataMapParameters); + checkDataGramPane(); // create datagram pane if a binary store already added. + sayHScale(); + + } + + /** + * Adds a settings pane for the data gram if a binary store is present. + */ + public void checkDataGramPane(){ + if (BinaryStore.findBinaryStoreControl()!=null){ + DatagramManager dataGramManager=BinaryStore.findBinaryStoreControl().getDatagramManager(); + if (dataGramManager!=null && dataGramSettingsPane==null) { + PamVBox datagramholder = new PamVBox(); + datagramholder.setSpacing(5); + Label datagramLabel = new Label("Datagrams"); + PamGuiManagerFX.titleFont2style(datagramLabel); + datagramholder.getChildren().add(datagramLabel); + datagramholder.getChildren().add(createDatagramPane(dataGramManager)); + + holder.getChildren().add(this.dataGramSettingsPane = datagramholder); + } + } + else { + holder.getChildren().remove(this.dataGramSettingsPane); + datagramBox=null; + } + } + + + /** + * Create the a datagram combo box to change the size of the datagram + * @param dataGramManager - the datagram manager for the current biinary store. + * @return a combo box with datagram bin sizes. + */ + private ComboBox createDataGramBinPane(DatagramManager dataGramManager){ + + dataGramComboBox=new ComboBox(createDurationList(dataGramManager)); + + dataGramComboBox.getSelectionModel().select(durationToString(dataGramManager.getDatagramSettings().datagramSeconds*1000L)); + dataGramComboBox.valueProperty().addListener(( ov, t, t1) -> { + if (t==t1) return; + else { + PamController.getInstance(); + boolean ans=PamDialogFX.showWarning(PamController.getMainStage(), "Warning", "Recalculating the datagram for a large dataset may take a long time" + + "Are you sure you want to continue"); + if (ans){ + int index=dataGramComboBox.getSelectionModel().getSelectedIndex(); + //messy- datagramSeconfds should be in millis but left for backwards compatibility. + dataGramManager.getDatagramSettings().datagramSeconds=(int) (DatagramSettings.defaultDatagramSeconds[index]/1000); + dataGramManager.updateDatagrams(); + dataMapPane.repaintAll(); + } + } + }); + + return dataGramComboBox; + + } + + /** + * Convert duration to string. + * @param duration in millis. + */ + private String durationToString(long duration){ + return (" " + PamCalendar.formatDuration(duration)); + } + + /** + * Create list of load times. + * @return list of load times duration. + */ + private ObservableList createDurationList(DatagramManager dataGramManager){ + ObservableList loadTimeList=FXCollections.observableArrayList(); + for (int i=0; i datagramBinsBox = createDataGramBinPane(dataGramManager); + + //Pane for colouring datagrams. + dataGramBox=new ComboBox (); + + //find all datagrams. + updateDataStreamBox(); + + dataGramBox.setOnAction((action)->{ + dataGramColPane.setDataStreamPanel(dataMapPane.getDataStreamPane(dataGramBox.getSelectionModel().getSelectedIndex())); + }); + + //holds settings for the datagram + dataGramColPane = new DataGramColPane(); + + PamGridPane holder = new PamGridPane(); + holder.setHgap(5); + holder.setVgap(5); + //holder.setGridLinesVisible(true); + + int row = 0; + + holder.add(dataGramLabel, 0,row); + holder.add(datagramBinsBox, 1, row); + + row++; + + holder.add(new Label("Select datagram"), 0, row); + holder.add(dataGramBox, 1, row); + + row++; + + GridPane.setHgrow(dataGramColPane, Priority.ALWAYS); + holder.add(dataGramColPane, 0, row); + + //dunno why this always had to be set after the child has been added to work. + GridPane.setColumnSpan(dataGramColPane, 3); + + //hack to make sure the third column of the gris expands to fit the pane + ColumnConstraints rightCol = new ColumnConstraints(); + rightCol.setHgrow(Priority.ALWAYS); + holder.getColumnConstraints().addAll(new ColumnConstraints(), new ColumnConstraints(), rightCol); + + return holder; + + } + + private void updateDataStreamBox() { + System.out.println("UPDATE DATA STREAM BOX: " + this.dataMapPane.getNumDataStreamPanes()); + for (int i=0; i{ + + }); + + //Change the colour of the colour slider when combo box is changed. + colorBox.valueProperty().addListener(new ChangeListener() { + @Override public void changed(ObservableValue ov, String t, String t1) { + //change the colour of the colour range slider. + setColours(); + } + }); + + + colorBox.setValue(ColourArray.getName(getColourArrayType())); + + //need to set up alignment properly. //FIXME- a bit messy + BorderPane.setAlignment(colorBox, Pos.CENTER); + //sliderPane.setPadding(new Insets(10,0,0,0)); + BorderPane.setMargin(colorBox, new Insets(0,5,0,5)); + + this.setCenter(colourSlider); + this.setRight(colorBox); + + //set up so the correct color + colorBox.setValue(ColourArray.getName(getColourArrayType())); + colourSlider.setColourArrayType(getColourArrayType()); + + } + + + public void setDataStreamPanel(DataStreamPaneFX selectedItem) { + this.dataStreamPane=selectedItem; + //TODO - set the colours here. + + } + + + /** + * Set colours depending on current colour selection in combo box. + */ + private void setColours(){ + this.setColourArrayType(ColourArray.getColourArrayType(colorBox.getValue())); + colourSlider.setColourArrayType(getColourArrayType()); + } + + + public ColourArrayType getColourArrayType() { + return colorArray; + } + + public void setColourArrayType(ColourArrayType colourArrayType) { + this.colorArray = colourArrayType; + } + + + } + + + /** + * Create a pane to change vertical scales. + * @return pamne with controls to change vertical scales. + */ + private PamGridPane createScalePane() { + + PamGridPane controlPane=new PamGridPane(); + controlPane.setHgap(5); + controlPane.setVgap(5); + + // //create time scale controls + // controlPane.add(new Label("Time window"),0,1); + // + // //create time slider + // timeSlider=new Slider(0, timeScaleChoices.length-1, 1); + // timeSlider.setShowTickLabels(true); + // timeSlider.setShowTickMarks(true); + // timeSlider.setMajorTickUnit(1); + // + // timeSlider.setLabelFormatter(new ScaleStringConverter()); + // + //// PamGridPane.setHalignment(timeSlider, Pos.BOTTOM_CENTER); + // + // controlPane.add(timeSlider,1,1); + // //add listener to time slider to change datamap. + // timeSlider.valueProperty().addListener((ov, oldVal, newVal)->{ + // sayHScale(); + // dataMapPane.scaleChanged(); + // }); + // + // controlPane.add(timeScaleLabel=new Label(""),3,1); + + //create vertical scale controls + + scaleBox=new ComboBox (); + scaleBox.getItems().add("No Scaling"); + scaleBox.getItems().add("per Second"); + scaleBox.getItems().add("per Minute"); + scaleBox.getItems().add("per Hour"); + scaleBox.getItems().add("per Day"); + + controlPane.add(new Label("Show detections "),0,0); + controlPane.add(scaleBox,1,0); +// scaleBox.setPrefWidth(200); + + scaleBox.valueProperty().addListener((ov, oldVal, newVal)->{ + dataMapPane.scaleChanged(); + }); + + + logScaleToggle=new PamToggleSwitch("Log Scale"); + logScaleToggle.selectedProperty().addListener((obsVal, oldVal, newVal)->{ + dataMapPane.scaleChanged(); + }); + + + controlPane.add(logScaleToggle,0,1); + + + return controlPane; + } + + /** + * Show the horizontal scale. + */ + private void sayHScale() { +// double hChoice = timeScaleChoices[this.]; +// timeScaleLabel.setText(String.format("%s pixs/hour", new Double(timeScaleChoices[(int) hChoice]).toString())); + } + + //HACK use setting flag to avoid immediate callback which overwrites changes 2 and 3 ! + boolean setting = false; + + public void setParams(DataMapParameters dataMapParameters) { + setting = true; +// timeSlider.setValue(dataMapParameters.hScaleChoice); + scaleBox.getSelectionModel().select(dataMapParameters.vScaleChoice); + logScaleToggle.setSelected(dataMapParameters.vLogScale); + + //make sure the combo box has correct datastreams + updateDataStreamBox(); + + setting = false; + } + + + public DataMapParameters getParams(DataMapParameters dataMapParameters) { + if (setting) return dataMapParameters; +// dataMapParameters.hScaleChoice = (int) timeSlider.getValue(); + dataMapParameters.vScaleChoice = scaleBox.getSelectionModel().getSelectedIndex(); + dataMapParameters.vLogScale = logScaleToggle.isSelected(); + + return dataMapParameters; + } + + @Override + public String getName() { + return "Datamap settings"; + } + + @Override + public Node getContentNode() { + // TODO Auto-generated method stub + return mainPain; + } + + @Override + public void paneInitialized() { + // TODO Auto-generated method stub + + } + + +} diff --git a/src/dataMap/layoutFX/DataStreamPaneFX.java b/src/dataMap/layoutFX/DataStreamPaneFX.java index 8d923f0b..884b1128 100644 --- a/src/dataMap/layoutFX/DataStreamPaneFX.java +++ b/src/dataMap/layoutFX/DataStreamPaneFX.java @@ -13,7 +13,6 @@ import dataMap.DataMapControl; import dataMap.DataMapDrawing; import dataMap.OfflineDataMap; import dataMap.OfflineDataMapPoint; -import dataPlotsFX.layout.AxisPane; import javafx.animation.KeyFrame; import javafx.animation.Timeline; import javafx.geometry.Orientation; @@ -29,9 +28,10 @@ import javafx.scene.paint.Color; import javafx.util.Duration; import PamController.OfflineDataStore; import PamController.PamController; -import PamUtils.PamCalendar; import PamguardMVC.PamDataBlock; +import pamViewFX.fxGlyphs.PamGlyphDude; import pamViewFX.fxNodes.PamBorderPane; +import pamViewFX.fxNodes.PamButton; import pamViewFX.fxNodes.PamHBox; import pamViewFX.fxNodes.pamAxis.PamAxisFX; import pamViewFX.fxNodes.pamAxis.PamAxisPane; @@ -42,7 +42,9 @@ public class DataStreamPaneFX extends PamBorderPane { /** * The preferred width of the axis. */ - public static double axisPrefWidth=80; + public static double PREF_AXIS_WIDTH=80; + + public static double PREF_HEADER_HEIGHT=20; /** * Reference to the data map control @@ -119,6 +121,8 @@ public class DataStreamPaneFX extends PamBorderPane { */ private Timeline timeline; + private PamButton showButton; + /** * Constructor for the data stream pane. * @param dataMapControl - the DataMapControl control the DataStreamPaneFX belongs to @@ -134,6 +138,7 @@ public class DataStreamPaneFX extends PamBorderPane { dataGraph = new DataGraphFX(); dataGraph.setupAxis(); dataName = new DataName(); + dataName.setName(dataBlock.getDataName()); this.setTop(topPane=createTopPane()); this.setCenter(dataGraph); @@ -147,10 +152,39 @@ public class DataStreamPaneFX extends PamBorderPane { topPane.getStyleClass().add("pane-opaque"); topPane.getChildren().add(new Label(this.dataBlock.getDataName())); topPane.setAlignment(Pos.CENTER); - return topPane; + + PamBorderPane pane = new PamBorderPane(); + + pane.setCenter(topPane); + + showButton = new PamButton(); + showButton.setStyle("-fx-padding: 0 10 0 10; -fx-border-radius: 0 0 0 0; -fx-background-radius: 0 0 0 0;"); + showButton.setOnAction((action)->{ + this.setCollapsed(!this.isCollapsed()); + setButtonGraphic(); + }); + setButtonGraphic(); + + pane.setLeft(showButton); + + pane.setPrefHeight(PREF_HEADER_HEIGHT); + + return pane; } + + + private void setButtonGraphic() { + if (this.isCollapsed()) { + showButton.setGraphic(PamGlyphDude.createPamIcon("mdi2c-chevron-down", (int) PREF_HEADER_HEIGHT-2)); + } + else { + showButton.setGraphic(PamGlyphDude.createPamIcon("mdi2c-chevron-up", (int) PREF_HEADER_HEIGHT-2)); + } + + } + /** * @return the dataGraph */ @@ -242,6 +276,8 @@ public class DataStreamPaneFX extends PamBorderPane { private DataGraphFX() { createDataGraph(); addDataGraphMouse(); + + } @@ -269,7 +305,10 @@ public class DataStreamPaneFX extends PamBorderPane { }); canvasHolder.setOnScroll(e->{ - wheelMoved(e); + //only change colours of the control key is down. + if (e.isControlDown()) { + wheelMoved(e); + } }); } @@ -308,6 +347,8 @@ public class DataStreamPaneFX extends PamBorderPane { //create canvas for overlaid drawings drawCanvas=new Canvas(90,90); + plotCanvas. getGraphicsContext2D(). setImageSmoothing(false); + Pane pane = new Pane(); pane.getChildren().add(plotCanvas); pane.getChildren().add(drawCanvas); @@ -349,7 +390,7 @@ public class DataStreamPaneFX extends PamBorderPane { axisPane=new PamAxisPane(datastreamAxis, Orientation.VERTICAL); axisPane.getStyleClass().add("pane"); axisPane.setOrientation(Orientation.VERTICAL); - axisPane.setPrefWidth(DataStreamPaneFX.axisPrefWidth); + axisPane.setPrefWidth(DataStreamPaneFX.PREF_AXIS_WIDTH); axisPane.setStrokeColor(Color.BLACK); this.setLeft(axisPane); @@ -386,7 +427,7 @@ public class DataStreamPaneFX extends PamBorderPane { long time2 = System.currentTimeMillis(); - //System.out.println("Paint Canvas: " + this + " " + System.currentTimeMillis() + " " + (time2-time1)); +// System.out.println("Paint Canvas: " + this + " " + System.currentTimeMillis() + " " + (time2-time1)); } @@ -536,7 +577,8 @@ public class DataStreamPaneFX extends PamBorderPane { } - private void datagramPaint3D(GraphicsContext g) { + private synchronized void datagramPaint3D(GraphicsContext g) { +// System.out.println("Paint 3D Canvas: " + this + " " + System.currentTimeMillis()); /* * hopefully, there will be datagram data for this block, so do a pretty @@ -949,6 +991,16 @@ public class DataStreamPaneFX extends PamBorderPane { } public class DataName { + + private String name; + + public String getName() { + return name; + } + + public void setName(String dataName) { + this.name=dataName; + } } @@ -1056,6 +1108,16 @@ public class DataStreamPaneFX extends PamBorderPane { */ public void setCollapsed(boolean collapsed) { this.collapsed=collapsed; + if (collapsed) { + this.setCenter(null); + this.setMaxHeight(PREF_HEADER_HEIGHT); + + } + else { + this.setCenter(dataGraph); + this.setMaxHeight(-1); + + } } /** diff --git a/src/dataMap/layoutFX/ScalePaneFX.java b/src/dataMap/layoutFX/ScalePaneFX.java deleted file mode 100644 index 49569edb..00000000 --- a/src/dataMap/layoutFX/ScalePaneFX.java +++ /dev/null @@ -1,293 +0,0 @@ -package dataMap.layoutFX; - -import dataGram.DatagramManager; -import dataGram.DatagramSettings; -import dataMap.DataMapControl; -import dataMap.DataMapParameters; -import PamController.PamController; -import PamUtils.PamCalendar; -import binaryFileStorage.BinaryStore; -import javafx.collections.FXCollections; -import javafx.collections.ObservableList; -import javafx.geometry.Pos; -import javafx.scene.Node; -import javafx.scene.control.CheckBox; -import javafx.scene.control.ComboBox; -import javafx.scene.control.Label; -import javafx.scene.control.Slider; -import javafx.scene.layout.GridPane; -import javafx.scene.layout.Pane; -import javafx.util.StringConverter; -import pamViewFX.PamGuiManagerFX; -import pamViewFX.fxNodes.PamBorderPane; -import pamViewFX.fxNodes.PamGridPane; -import pamViewFX.fxNodes.PamHBox; -import pamViewFX.fxNodes.PamVBox; -import pamViewFX.fxNodes.pamDialogFX.PamDialogFX; -import pamViewFX.fxNodes.sliders.PamSlider; -import pamViewFX.fxNodes.utilityPanes.PamToggleSwitch; - -/** - * Allows uses to change the horizontal and vertical scales on the data map. - * - * @author Jamie Macaulay - * - */ -public class ScalePaneFX extends PamBorderPane { - - /* - * Reference to the data map control. - */ - private DataMapControl dataMapControl; - - /** - * Reference to the dataMapPane. - */ - private DataMapPaneFX dataMapPane; - - /** - * The slider which determines time scale. - */ - private Slider timeSlider; - - /** - * Shows the time scale in pix/hour - */ - private Label timeScaleLabel; - - /** - * Selects unit count on vertical scale - */ - private ComboBox scaleBox; - - /** - * Check box for log vertical scale. - */ - private PamToggleSwitch logScaleBox; - - /** - * The chosen time values. - */ - private double[] timeScaleChoices = DataMapParameters.hScaleChoices; - - /** - * Combo box holding options to channge the datargam bin size - */ - private ComboBox datagramBox; - - /** - * Holds a list of times for the datagram bin size. - */ - private ComboBox dataGramComboBox; - - /** - * Holds everything. - */ - private PamVBox holder; - - /** - * Grid pane with settings for the data scales - */ - private PamGridPane scaleSettingsPane; - - private Label dataGramLabel; - - - - public ScalePaneFX(DataMapControl dataMapControl, DataMapPaneFX dataMapPane) { - this.dataMapControl = dataMapControl; - this.dataMapPane = dataMapPane; - -// //create the holder pane. -// PamVBox vBoxHolder=new PamVBox(); -// vBoxHolder.setSpacing(5); -// Label title=new Label("Scales"); -// PamGuiManagerFX.titleFont2style(title); -// vBoxHolder.getChildren().addAll(title, controlPane); - - holder=new PamVBox(); - holder.setSpacing(20); - holder.getChildren().add(scaleSettingsPane = createScalePane()); - - this.setCenter(holder); - - //set params for the pane - setParams(dataMapControl.dataMapParameters); - checkDataGramPane(); // create datagram pane if a binary store already added. - sayHScale(); - - } - - /** - * Adds a settings pane for the datagram if a binary store is present. - */ - public void checkDataGramPane(){ - if (BinaryStore.findBinaryStoreControl()!=null){ - DatagramManager dataGramManager=BinaryStore.findBinaryStoreControl().getDatagramManager(); - if (dataGramManager!=null && datagramBox==null) { - datagramBox=createDatagramPane(dataGramManager); - scaleSettingsPane.add(dataGramLabel= new Label("Datagram bin size"), 0, 2); - scaleSettingsPane.add(dataGramComboBox, 1, 2); - } - } - else { - scaleSettingsPane.getChildren().remove(dataGramLabel); - scaleSettingsPane.getChildren().remove(dataGramComboBox); - - datagramBox=null; - } - } - - - /** - * Create the a datagram combo box to change the size of the datagram - * @param dataGramManager - the datagram manager for the current biinary store. - * @return a combo box with datagram bin sizes. - */ - private ComboBox createDatagramPane(DatagramManager dataGramManager){ - - dataGramComboBox=new ComboBox(createDurationList(dataGramManager)); - - dataGramComboBox.getSelectionModel().select(durationToString(dataGramManager.getDatagramSettings().datagramSeconds*1000L)); - dataGramComboBox.valueProperty().addListener(( ov, t, t1) -> { - if (t==t1) return; - else { - PamController.getInstance(); - boolean ans=PamDialogFX.showWarning(PamController.getMainStage(), "Warning", "Recalculating the datagram for a large dataset may take a long time
" + - "Are you sure you want to continue ?"); - if (ans){ - int index=dataGramComboBox.getSelectionModel().getSelectedIndex(); - //messy- datagramSeconfds should be in millis but left for backwards compatibility. - dataGramManager.getDatagramSettings().datagramSeconds=(int) (DatagramSettings.defaultDatagramSeconds[index]/1000); - dataGramManager.updateDatagrams(); - dataMapPane.repaintAll(); - } - } - }); - - return dataGramComboBox; - - } - - /** - * Convert duration to string. - * @param duration in millis. - */ - private String durationToString(long duration){ - return (" " + PamCalendar.formatDuration(duration)); - } - - /** - * Create list of load times. - * @return list of load times duration. - */ - private ObservableList createDurationList(DatagramManager dataGramManager){ - ObservableList loadTimeList=FXCollections.observableArrayList(); - for (int i=0; i{ - sayHScale(); - dataMapPane.scaleChanged(); - }); - - controlPane.add(timeScaleLabel=new Label(""),3,1); - - //create vertical scale controls - controlPane.add(new Label("Data counts"),0,0); - - - scaleBox=new ComboBox (); - scaleBox.getItems().add("No Scaling"); - scaleBox.getItems().add("per Second"); - scaleBox.getItems().add("per Minute"); - scaleBox.getItems().add("per Hour"); - scaleBox.getItems().add("per Day"); - controlPane.add(scaleBox,1,0); - scaleBox.setPrefWidth(200); - - scaleBox.valueProperty().addListener((ov, oldVal, newVal)->{ - dataMapPane.scaleChanged(); - }); - - - logScaleBox=new PamToggleSwitch("Log Scale"); - logScaleBox.selectedProperty().addListener((obsVal, oldVal, newVal)->{ - dataMapPane.scaleChanged(); - }); - - - controlPane.add(logScaleBox,3,0); - - - return controlPane; - } - - class ScaleStringConverter extends StringConverter { - - @Override - public String toString(Double object) { - return String.valueOf(timeScaleChoices[object.intValue()]); - } - - @Override - public Double fromString(String string) { - return Double.valueOf(string); - } - - } - - /** - * Show the horizontal scale. - */ - private void sayHScale() { - double hChoice = timeSlider.getValue(); - timeScaleLabel.setText(String.format("%s pixs/hour", new Double(timeScaleChoices[(int) hChoice]).toString())); - } - - //HACK use setting flag to avoid immediate callback which overwrites changes 2 and 3 ! - boolean setting = false; - - public void setParams(DataMapParameters dataMapParameters) { - setting = true; - timeSlider.setValue(dataMapParameters.hScaleChoice); - scaleBox.getSelectionModel().select(dataMapParameters.vScaleChoice); - logScaleBox.setSelected(dataMapParameters.vLogScale); - setting = false; - } - - - public void getParams(DataMapParameters dataMapParameters) { - if (setting) return; - dataMapParameters.hScaleChoice = (int) timeSlider.getValue(); - dataMapParameters.vScaleChoice = scaleBox.getSelectionModel().getSelectedIndex(); - dataMapParameters.vLogScale = logScaleBox.isSelected(); - } - - -} diff --git a/src/dataMap/layoutFX/ScrollingDataPaneFX.java b/src/dataMap/layoutFX/ScrollingDataPaneFX.java index 2faffbdb..d8f353b8 100644 --- a/src/dataMap/layoutFX/ScrollingDataPaneFX.java +++ b/src/dataMap/layoutFX/ScrollingDataPaneFX.java @@ -12,6 +12,7 @@ import javafx.geometry.Side; import javafx.scene.Node; import javafx.scene.control.Label; import javafx.scene.control.ScrollBar; +import javafx.scene.control.ScrollPane.ScrollBarPolicy; import javafx.scene.paint.Color; import javafx.util.Duration; import PamguardMVC.PamDataBlock; @@ -21,25 +22,26 @@ import pamViewFX.fxNodes.PamColorsFX; import pamViewFX.fxNodes.PamScrollPane; import pamViewFX.fxNodes.PamVBox; import pamViewFX.fxNodes.pamAxis.PamDateAxis; +import pamViewFX.fxNodes.pamScrollers.acousticScroller.ScrollBarPane; public class ScrollingDataPaneFX extends PamBorderPane { - + /** * Standard millis to wait for repaint. - * Do not ake this too high i.e. above 50 or the display gets very jerky + * Do not make this too high i.e. above 50 or the display gets very jerky */ public static final long REPAINTMILLIS = 50; /** * The default expanded hieght for each pane. */ - private static final int DATASTREAMPANE_HEIGHT = 200; + private static final int DATASTREAMPANE_HEIGHT = 220; /** * Reference to the DataMapControl. */ private DataMapControl dataMapControl; - + /** * Reference to the DataMapPaneFX. */ @@ -49,19 +51,19 @@ public class ScrollingDataPaneFX extends PamBorderPane { * The scroll pane everything sits in */ private PamScrollPane mainScrollPane; - + /** * List of panes- each shows an individual data stream. */ private ArrayList dataStreamPanels = new ArrayList(); - + /** * Split pane whihc holds different graphs. */ private PamVBox dataPanePanes; private ArrayList offlineDataStores; - + /** * Time stamp in millis of start of datamap display */ @@ -71,24 +73,19 @@ public class ScrollingDataPaneFX extends PamBorderPane { * Time stamp in millis of end of datamap display */ private long screenEndMillis = -1; - + private double screenSeconds; /** * Scroll bar for time (horizontal) */ - private ScrollBar timeScrollBar; + private DataMapScrollBar timeScrollBar; - /** - * Settings strip at top of the display. Shows all sorts of detailed info such cursor position and start and end times. - */ - private SettingsStripFX settingsStrip; - /** * Shows the start time of the scroll position */ private Label scrollEndLabel; - + /** * Shows the end time of the scroll position. */ @@ -110,7 +107,6 @@ public class ScrollingDataPaneFX extends PamBorderPane { DataMapPaneFX dataMapPaneFX) { this.dataMapControl = dataMapControl; this.dataMapPaneFX = dataMapPaneFX; - settingsStrip=new SettingsStripFX(this); this.setCenter(createScrollingDataPane()); } @@ -119,13 +115,14 @@ public class ScrollingDataPaneFX extends PamBorderPane { * @return the scrolling pane. */ private Node createScrollingDataPane() { - + holder=new PamBorderPane(); //create the main scroll pane mainScrollPane = new PamScrollPane(); - - + mainScrollPane.setHbarPolicy(ScrollBarPolicy.NEVER); + + //create the split pane to hold the graphs. dataPanePanes=new PamVBox(); //dataPanePanes.setOrientation(Orientation.VERTICAL); @@ -134,48 +131,54 @@ public class ScrollingDataPaneFX extends PamBorderPane { mainScrollPane.setContent(dataPanePanes); //we have a custom scroll bar for horizontal stuff. -// mainScrollPane.setHbarPolicy(ScrollBarPolicy.NEVER); - -// ///TEMP/// -// Button buttonTest=new Button("Test Map"); -// buttonTest.setOnAction((action)->{ -// this.dataMapControl.findDataSources(); -// }); -// holder.setTop(buttonTest); -// ////////// - - holder.setCenter(mainScrollPane); - holder.setBottom(createScrollBar()); - -// PamButton test = new PamButton("Test"); -// test.setOnAction((action)->{ -// updateScrollBar(); -// }); -// holder.setLeft(test); + // mainScrollPane.setHbarPolicy(ScrollBarPolicy.NEVER); + + // ///TEMP/// + // Button buttonTest=new Button("Test Map"); + // buttonTest.setOnAction((action)->{ + // this.dataMapControl.findDataSources(); + // }); + // holder.setTop(buttonTest); + // ////////// + + + // PamButton test = new PamButton("Test"); + // test.setOnAction((action)->{ + // updateScrollBar(); + // }); + // holder.setLeft(test); + - setupScrollBar(); - //finally make sure the scroll bar recalculates stuff when holder changes size holder.widthProperty().addListener((change)->{ notifyScrollChange(300); }); - - + + dateAxis = new PamDateAxis(); dateAxis.setAutoRanging(false); dateAxis.setLabel("Time"); dateAxis.setSide(Side.TOP); dateAxis.setAnimated(false); dateAxis.setMinHeight(50); -// dateAxis.prefWidthProperty().bind(scrollingDataPanel.widthProperty()); -// dateAxis.setStyle("-fx-background-color: ORANGE;"); + // dateAxis.prefWidthProperty().bind(scrollingDataPanel.widthProperty()); + // dateAxis.setStyle("-fx-background-color: ORANGE;"); dateAxis.setForceZeroInRange(false); - holder.setTop(dateAxis); - + + + PamVBox vBox = new PamVBox(); + vBox.getChildren().add(createScrollBar()); + vBox.getChildren().add(dateAxis); + + holder.setTop(vBox); + holder.setCenter(mainScrollPane); + + setupScrollBar(); + return holder; } - + /** * Updates the scrollbar. @@ -183,19 +186,19 @@ public class ScrollingDataPaneFX extends PamBorderPane { public void updateScrollBar() { setupScrollBar(); updateScrollBarText(); - + calcStartEndMillis(); updateScrollBarText(); notifyScrollChange(); } - + /** * Create the horizontal scroll bar for scrolling through time. * @return the horizontal scroll bar. */ private PamBorderPane createScrollBar(){ PamBorderPane holder=new PamBorderPane(); - + //create a pane to show start and end times PamBorderPane timeLabelPane=new PamBorderPane(); scrollStartLabel=new Label(); @@ -206,32 +209,45 @@ public class ScrollingDataPaneFX extends PamBorderPane { scrollEndLabel.setText(PamCalendar.formatDateTime(screenEndMillis)); timeLabelPane.setPadding(new Insets(2, 10, 2, 10)); //bit of padding to look better - + //create the scroll bar and listeners. - timeScrollBar=new ScrollBar(); - timeScrollBar.valueProperty().addListener((obs_val, old_val, new_val)->{ + timeScrollBar=new DataMapScrollBar(this.dataMapControl); + timeScrollBar.addValueListener((obs_val, old_val, new_val)->{ +// System.out.println("Scroll bar seconds: " + timeScrollBar.getCurrentValue() + " vis amount: " + timeScrollBar.visibleAmountProperty().get()); calcStartEndMillis(); updateScrollBarText(); notifyScrollChange(); }); + + timeScrollBar.getTextBox().setPrefColumnCount(15); + timeScrollBar.getTextBox().setPrefWidth(100); + + timeScrollBar.setPrefHeight(50); - timeScrollBar.setPrefHeight(20); - + //set this to zero just so that we know if it has been set or not + timeScrollBar.setVisibleAmount(0.); + holder.setCenter(timeScrollBar); holder.setBottom(timeLabelPane); - + return holder; } - + /** * Calculate the start and millis based on scroll position and screen seconds. */ private void calcStartEndMillis(){ screenStartMillis = (long) (dataMapControl.getFirstTime() + - timeScrollBar.getValue() * 1000L); - screenEndMillis = screenStartMillis + (long) (screenSeconds * 1000); + timeScrollBar.getCurrentValue()); + screenEndMillis = (long) (screenStartMillis + timeScrollBar.getVisibleAmount()); + + double pixsPerHour = getPixelsPerHour(); + double pixsPerSecond = pixsPerHour / 3600; + double screenWidth = getPlotWidth(); + screenSeconds = screenWidth / Math.min(600. / 3600, pixsPerSecond); + } - + /** * Update the text in the scroll bar. Shows the start and end time of the current screen. */ @@ -248,14 +264,14 @@ public class ScrollingDataPaneFX extends PamBorderPane { for (int i = 0; i < dataStreamPanels.size(); i++) { dataStreamPanels.get(i).scrollChanged(); } - settingsStrip.scrollChanged(); - +// settingsStrip.scrollChanged(); + updateDateAxis(); } - + Timeline timeline; long lastTime = 0; - + /** * Notify all panels and the settings strip that the scroll bar moved - but have a timer to wait to not call too often. */ @@ -273,11 +289,11 @@ public class ScrollingDataPaneFX extends PamBorderPane { timeline.play(); return; } - + lastTime=currentTime; - + updateDateAxis(); - + } private void updateDateAxis() { @@ -285,7 +301,7 @@ public class ScrollingDataPaneFX extends PamBorderPane { dateAxis.setUpperBound(screenEndMillis); dateAxis.setLowerBound(screenStartMillis); double[] ticks = dateAxis.recalculateTicks(); -// System.out.println("Ticks: " + (ticks[3]/1000/3600) + "hours"); + // System.out.println("Ticks: " + (ticks[3]/1000/3600) + "hours"); dateAxis.setTickUnit(ticks[3]); } @@ -294,30 +310,30 @@ public class ScrollingDataPaneFX extends PamBorderPane { * @return the number of DataStreamPanes created. */ public synchronized int createDataGraphs() { - //clear the panes from list and split pane. - dataStreamPanels.clear(); - dataPanePanes.getChildren().clear(); - - //now create new set of data stream panes. - ArrayList dataBlocks = dataMapControl.getMappedDataBlocks(); - if (dataBlocks == null) { - System.out.println("DataMapPaneFX:Create Data Graphs: Datablocks are null"); - return 0; - } - DataStreamPaneFX aStreamPanel; - for (int i = 0; i < dataBlocks.size(); i++) { - aStreamPanel = new DataStreamPaneFX(dataMapControl, this, dataBlocks.get(i)); - dataStreamPanels.add(aStreamPanel); - dataStreamPanels.get(i).setMinHeight(DATASTREAMPANE_HEIGHT); - //now add to a split pane. - //SplitPane.setResizableWithParent(aStreamPanel, true); - dataPanePanes.getChildren().add(aStreamPanel); - //dataPanePanes.setDividerPosition(0,1.0/dataBlocks.size()); - } + //clear the panes from list and split pane. + dataStreamPanels.clear(); + dataPanePanes.getChildren().clear(); - return dataBlocks.size(); + //now create new set of data stream panes. + ArrayList dataBlocks = dataMapControl.getMappedDataBlocks(); + if (dataBlocks == null) { + System.out.println("DataMapPaneFX:Create Data Graphs: Datablocks are null"); + return 0; + } + DataStreamPaneFX aStreamPanel; + for (int i = 0; i < dataBlocks.size(); i++) { + aStreamPanel = new DataStreamPaneFX(dataMapControl, this, dataBlocks.get(i)); + dataStreamPanels.add(aStreamPanel); + dataStreamPanels.get(i).setPrefHeight(DATASTREAMPANE_HEIGHT); + //now add to a split pane. + //SplitPane.setResizableWithParent(aStreamPanel, true); + dataPanePanes.getChildren().add(aStreamPanel); + //dataPanePanes.setDividerPosition(0,1.0/dataBlocks.size()); + } + + return dataBlocks.size(); } - + /*** * Get the number of panes which are expanded. @@ -341,39 +357,50 @@ public class ScrollingDataPaneFX extends PamBorderPane { createDataGraphs(); setupScrollBar(); } - + private void setupScrollBar() { + /** * Do scrolling in seconds - will give up to 68 years with a * 32 bit integer control of scroll bar. milliseconds would give < 1 year ! */ - double currentPos = timeScrollBar.getValue(); + double currentPos = timeScrollBar.getCurrentValue(); long dataStart = dataMapControl.getFirstTime(); long dataEnd = dataMapControl.getLastTime(); double dataSeconds = ((dataEnd-dataStart)/1000) + 1; - double pixsPerHour = getPixelsPerHour(); - double pixsPerSecond = pixsPerHour / 3600; - double screenWidth = getPlotWidth(); - screenSeconds = screenWidth / pixsPerSecond; - if (dataStart == Long.MAX_VALUE || screenSeconds >= dataSeconds) { - //System.out.println("dataSeconds: "+dataSeconds+ " pixsPerHour: " +pixsPerHour+" screenWidth: "+screenWidth+" screenSeconds "+screenSeconds+ " holder width: "+holder.getWidth()); - /* - * hide the scroll bar and stretch the display to fit the window - */ - timeScrollBar.setVisible(false); - screenStartMillis = dataStart; - screenEndMillis = dataEnd; - } - else { - //System.out.println("dataSeconds: "+dataSeconds+ " pixsPerHour: " +pixsPerHour+" screenWidth: "+screenWidth+" screenSeconds "+screenSeconds+" holder width: "+holder.getWidth()); - timeScrollBar.setVisible(true); - timeScrollBar.setMax(0); - timeScrollBar.setMax(Math.ceil(dataSeconds)); - timeScrollBar.setBlockIncrement(Math.max(1, screenSeconds * 4/5)); - timeScrollBar.setUnitIncrement(Math.max(1, screenSeconds / 20)); - timeScrollBar.setVisibleAmount(screenSeconds); - timeScrollBar.setValue(currentPos); + + calcStartEndMillis(); + + + + + // if (dataStart == Long.MAX_VALUE || screenSeconds >= dataSeconds) { + // System.out.println("dataSeconds1: "+dataSeconds+ " pixsPerHour: " +pixsPerHour+" screenWidth: "+screenWidth+" screenSeconds "+screenSeconds+ " holder width: "+holder.getWidth()); + // /* + // * hide the scroll bar and stretch the display to fit the window + // */ + // timeScrollBar.setVisible(true); + // screenStartMillis = dataStart; + // screenEndMillis = dataEnd; + // } + // else { +// System.out.println("dataSeconds2: "+dataSeconds+ " pixsPerHour: " +pixsPerHour+" screenWidth: "+screenWidth+" screenSeconds "+screenSeconds+" holder width: "+holder.getWidth()); + + timeScrollBar.setVisible(true); + timeScrollBar.setMinVal(0); + timeScrollBar.setMaxVal(Math.max(dataSeconds, screenSeconds)*1000L); + timeScrollBar.setBlockIncrement(Math.max(1, screenSeconds * 4/5)); + // timeScrollBar.setUnitIncrement(Math.max(1, screenSeconds / 20)); + + //there might already have a visible amount in which case we do not wish to change. This is a bit of a hack + //to figure out whether the visible amount has already been set. + if (timeScrollBar.getVisibleAmount()==0) { + timeScrollBar.setVisibleAmount(screenSeconds*1000L); } + timeScrollBar.setCurrentValue(currentPos); + + //now paint the canvas to show the data. + timeScrollBar.paintDataSummary(); } @@ -383,18 +410,18 @@ public class ScrollingDataPaneFX extends PamBorderPane { */ private double getPlotWidth() { //HACK- seems like there is a lyout delay in datstream panes. - return this.holder.getWidth()-DataStreamPaneFX.axisPrefWidth; -// if (dataStreamPanels.size()>0){ -// dataStreamPanels.get(0).layout(); -// return dataStreamPanels.get(0).getDataGraph().getPlotWidth(); -// } -// return 0; + return this.holder.getWidth()-DataStreamPaneFX.PREF_AXIS_WIDTH; + // if (dataStreamPanels.size()>0){ + // dataStreamPanels.get(0).layout(); + // return dataStreamPanels.get(0).getDataGraph().getPlotWidth(); + // } + // return 0; } public void scrollToData(PamDataBlock dataBlock) { long startTime = dataBlock.getCurrentViewDataStart(); int val = (int) ((startTime - getScreenStartMillis())/1000 - getScreenSeconds()/5) ; - timeScrollBar.setValue(val); + timeScrollBar.setCurrentValue(val); } /** @@ -410,10 +437,14 @@ public class ScrollingDataPaneFX extends PamBorderPane { public long getScreenEndMillis() { return screenEndMillis; } - - + + public double getPixelsPerHour() { - return dataMapControl.dataMapParameters.getPixeslPerHour(); +// System.out.println("Pixels per hour: " + dataMapControl.dataMapParameters.getPixeslPerHour() + " " + this.getPlotWidth()/(this.timeScrollBar.getVisibleAmount()/1000./3600.)); + //return dataMapControl.dataMapParameters.getPixeslPerHour(); + + return this.getPlotWidth()/(this.timeScrollBar.getVisibleAmount()/1000./3600.); + } /** @@ -422,7 +453,7 @@ public class ScrollingDataPaneFX extends PamBorderPane { public double getScreenSeconds() { return screenSeconds; } - + /** * Get a colour for the datastream. * @param dataSource @@ -456,13 +487,33 @@ public class ScrollingDataPaneFX extends PamBorderPane { return dataMapPaneFX; } - int lastHScaleChoice=-1; - public void scaleChange() { - if (lastHScaleChoice != dataMapControl.dataMapParameters.hScaleChoice) { - lastHScaleChoice = dataMapControl.dataMapParameters.hScaleChoice; - setupScrollBar(); - } - this.notifyScrollChange(); - } +// int lastHScaleChoice=-1; +// public void scaleChange() { +// if (lastHScaleChoice != dataMapControl.dataMapParameters.hScaleChoice) { +// lastHScaleChoice = dataMapControl.dataMapParameters.hScaleChoice; +// setupScrollBar(); +// } +// this.notifyScrollChange(); +// } + /** + * Get the current number of data stream panes + * @return the number of data stream panes + */ + public int getNumDataStreamPanes() { + return this.dataStreamPanels.size(); + } + + /** + * Get a data stream pane. + * @param n - the index of the data stream pane + * @return the data stream pane or null if the index is out of bounds. + */ + public DataStreamPaneFX getDataSyreamPane(int n) { + if (n{ @@ -132,7 +129,7 @@ public class WavExportOverlayMenu extends ExportOverlayMenu { */ private void writeWavFile(DetectionGroupSummary foundDataUnits, int selectedIndex, OverlayMark mark) { // animateButton() ; //start button animation. - wavExportManager.dataUnits2Wav(foundDataUnits, selectedIndex, mark); + wavExportManager.writeOverlayMarkWav(foundDataUnits, selectedIndex, mark); } @@ -148,7 +145,7 @@ public class WavExportOverlayMenu extends ExportOverlayMenu { rawDataBlock=foundDataUnits.getDataList().get(0).getParentDataBlock().getRawSourceDataBlock(); } //is there available raw data - if (mark!=null && WavFileExportManager.haveRawData(rawDataBlock, (long) mark.getLimits()[0], (long) mark.getLimits()[1])) { + if (mark!=null && WavDetExport.haveRawData(rawDataBlock, (long) mark.getLimits()[0], (long) mark.getLimits()[1])) { return true; } diff --git a/src/dataPlotsFX/overlaymark/popUpMenu/TDMenuPane.java b/src/dataPlotsFX/overlaymark/popUpMenu/TDMenuPane.java index 342c04ea..ab5d1797 100644 --- a/src/dataPlotsFX/overlaymark/popUpMenu/TDMenuPane.java +++ b/src/dataPlotsFX/overlaymark/popUpMenu/TDMenuPane.java @@ -12,7 +12,7 @@ import dataPlotsFX.layout.TDGraphFX.TDPlotPane; import dataPlotsFX.overlaymark.menuOptions.OverlayMenuItem; import dataPlotsFX.overlaymark.menuOptions.OverlayMenuManager; import detectiongrouplocaliser.DetectionGroupSummary; -import export.wavExport.WavFileExportManager; +import export.wavExport.WavDetExport; import javafx.geometry.Insets; import javafx.geometry.Orientation; import javafx.geometry.Pos; @@ -546,7 +546,7 @@ public class TDMenuPane extends PamBorderPane { PamRawDataBlock pamRawBlock = findRawSourceBlock(); // System.out.println("Pam raw data block: " + pamRawBlock); - if (WavFileExportManager.haveRawData(pamRawBlock, (long) overlayMarker.getCurrentMark().getLimits()[0], (long) overlayMarker.getCurrentMark().getLimits()[1])) { + if (WavDetExport.haveRawData(pamRawBlock, (long) overlayMarker.getCurrentMark().getLimits()[0], (long) overlayMarker.getCurrentMark().getLimits()[1])) { //System.out.println("Overaly Marker start X: " + overlayMarker.getCurrentMark().getLimits()[0] + " end: " + overlayMarker.getCurrentMark().getLimits()[1]); //System.out.println("Overlay Marker start Y: " + overlayMarker.getCurrentMark().getLimits()[2] + " end: " + overlayMarker.getCurrentMark().getLimits()[3]); groupDetectionDisplay.showRawData(pamRawBlock, overlayMarker.getCurrentMark().getLimits(), overlayMarker.getCurrentMark().getMarkChannels()); diff --git a/src/dataPlotsFX/rawClipDataPlot/RawClipDataInfo.java b/src/dataPlotsFX/rawClipDataPlot/RawClipDataInfo.java index ea4bb476..f6daeabc 100644 --- a/src/dataPlotsFX/rawClipDataPlot/RawClipDataInfo.java +++ b/src/dataPlotsFX/rawClipDataPlot/RawClipDataInfo.java @@ -2,6 +2,7 @@ package dataPlotsFX.rawClipDataPlot; import java.awt.geom.Path2D; +import java.io.Serializable; import java.util.ArrayList; import java.util.Arrays; @@ -14,7 +15,7 @@ import PamguardMVC.PamDataUnit; import PamguardMVC.RawDataHolder; import clipgenerator.ClipSpectrogram; import dataPlotsFX.TDSymbolChooserFX; -import dataPlotsFX.clickPlotFX.ClickSymbolChooserFX; +import dataPlotsFX.clickPlotFX.ClickDisplayParams; import dataPlotsFX.data.TDDataProviderFX; import dataPlotsFX.data.TDScaleInfo; import dataPlotsFX.data.generic.GenericDataPlotInfo; @@ -255,6 +256,36 @@ public class RawClipDataInfo extends GenericDataPlotInfo { // TODO Auto-generated method stub } + + /* (non-Javadoc) + * @see dataPlots.data.TDDataInfo#getStoredSettings() + */ + @Override + public Serializable getStoredSettings() { + return rawClipParams; + } + + /* (non-Javadoc) + * @see dataPlots.data.TDDataInfo#setStoredSettings(java.io.Serializable) + */ + @Override + public boolean setStoredSettings(Serializable storedSettings) { + if (RawClipParams.class.isAssignableFrom(storedSettings.getClass())) { + rawClipParams = (RawClipParams) storedSettings; + updateSettings(); + return true; + } + return false; + } + + /** + * Called whenever settings are updated. + */ + private void updateSettings() { + // TODO Auto-generated method stub + + } + } diff --git a/src/dataPlotsFX/scrollingPlot2D/Scrolling2DPlotDataFX.java b/src/dataPlotsFX/scrollingPlot2D/Scrolling2DPlotDataFX.java index 21d8b865..7e9e1039 100644 --- a/src/dataPlotsFX/scrollingPlot2D/Scrolling2DPlotDataFX.java +++ b/src/dataPlotsFX/scrollingPlot2D/Scrolling2DPlotDataFX.java @@ -925,6 +925,8 @@ public class Scrolling2DPlotDataFX { //reset transform in case of other drawings on base canvas. if (orientation == Orientation.VERTICAL) g2d.setTransform(horzAffine); + + } diff --git a/src/dataPlotsFX/spectrogramPlotFX/SpectrogramControlPane.java b/src/dataPlotsFX/spectrogramPlotFX/SpectrogramControlPane.java index e2844c60..b4e87926 100644 --- a/src/dataPlotsFX/spectrogramPlotFX/SpectrogramControlPane.java +++ b/src/dataPlotsFX/spectrogramPlotFX/SpectrogramControlPane.java @@ -22,9 +22,13 @@ import javafx.scene.layout.Pane; import javafx.scene.paint.Color; /** - * A general pane with a frequency slider, colour bar slider and colour combo box. These nodes are interconnected so when colour combo box is changed colours change. - * Other than that this pane does nothing but all slider value properties are available to be used in another class or subclass. This means this control pane should be - * available to be used for any spectrogram/3D colour image. + * A general pane with a frequency slider, colour bar slider and colour combo + * box. These nodes are interconnected so when colour combo box is changed + * colours change. Other than that this pane does nothing but all slider value + * properties are available to be used in another class or subclass. This means + * this control pane should be available to be used for any spectrogram/3D + * colour image. + * * @author Jamie Macaulay * */ diff --git a/src/detectionPlotFX/data/DDDataInfo.java b/src/detectionPlotFX/data/DDDataInfo.java index 01c59f79..e4e2d0e0 100644 --- a/src/detectionPlotFX/data/DDDataInfo.java +++ b/src/detectionPlotFX/data/DDDataInfo.java @@ -276,7 +276,7 @@ public abstract class DDDataInfo { getCurrentDetectionPlot().setupAxis((PamDataUnit) pamDataUnit, this.getHardSampleRate(), projector); getCurrentDetectionPlot().paintPlot((PamDataUnit) pamDataUnit, g, windowRect, projector, flag); - + } diff --git a/src/detectionPlotFX/layout/DetectionPlotDisplay.java b/src/detectionPlotFX/layout/DetectionPlotDisplay.java index 16120cee..3630bc4e 100644 --- a/src/detectionPlotFX/layout/DetectionPlotDisplay.java +++ b/src/detectionPlotFX/layout/DetectionPlotDisplay.java @@ -226,7 +226,7 @@ public class DetectionPlotDisplay extends PamBorderPane { * Called whenever the scroll values change */ void scrollBarChanged() { - if (detectionPlotProjector.enableScrollBar) { + if (detectionPlotProjector.isEnableScrollBar()) { detectionPlotProjector.setAxisMinMax(scrollBarPane.getCurrentValue(), scrollBarPane.getCurrentValue()+scrollBarPane.getVisibleAmount(), detectionPlotProjector.getScrollAxis()); drawCurrentUnit(); @@ -237,7 +237,7 @@ public class DetectionPlotDisplay extends PamBorderPane { * Repaint the scroll bar. */ private void repainScrollBar() { - if (currentDataInfo!=null && detectionPlotProjector.enableScrollBar && lastDetection!=null) { + if (currentDataInfo!=null && detectionPlotProjector.isEnableScrollBar() && lastDetection!=null) { currentDataInfo.drawData(scrollBarPane.getDrawCanvas().getGraphicsContext2D(), new Rectangle(0,0,scrollBarPane.getDrawCanvas().getWidth(),scrollBarPane.getDrawCanvas().getHeight()), this.detectionPlotProjector, this.lastDetection, DetectionPlot.SCROLLPANE_DRAW); @@ -355,6 +355,7 @@ public class DetectionPlotDisplay extends PamBorderPane { * the current data unit. */ public void setupScrollBar(PamDataUnit newDataUnit){ + System.out.println("SETUP SCROLL BAR:"); if (currentDataInfo!=null) { //important we put this here as it allows the plot to set up the scroll bar pane. @@ -382,12 +383,18 @@ public class DetectionPlotDisplay extends PamBorderPane { this.detectionPlotProjector, newDataUnit, DetectionPlot.SCROLLPANE_DRAW); } - } else { + System.out.println("Min scroll limit: " + detectionPlotProjector.getMinScrollLimit() + "max: " + detectionPlotProjector.getMaxScrollLimit()); //need this to ensure the axis change when scroll bar is not longer displayed. detectionPlotProjector.setAxisMinMax(detectionPlotProjector.getMinScrollLimit(), detectionPlotProjector.getMaxScrollLimit(), detectionPlotProjector.getScrollAxis()); + + //need to setup the axis as it takes it data fom the ploit projector. + if (currentDataInfo!=null) { + this.currentDataInfo.setupAxis(detectionPlotProjector, newDataUnit); + } + this.setTop(null); } } @@ -411,27 +418,32 @@ public class DetectionPlotDisplay extends PamBorderPane { *Draw the data unit. */ private void drawDataUnit(PamDataUnit newDataUnit) { - //Debug.out.println("DetectionPlotDisplay DrawDataUnit: " +newDataUnit); - if (currentDataInfo!=null){ - - //sometimes the axis just need a little push to make sure the pane and axis object bindings have been updated - for (int i=0; i implements DetectionPl */ @Override public void setupAxis(D pamDetection, double sR, DetectionPlotProjector plotProjector){ - //System.out.println("WaveformPlot: plotting the waveform" + waveform.length); + System.out.println("WaveformPlot.setupAxis plotting the waveform: " + getWaveform(pamDetection)[0].length); //all axis are used in the waveform plot except the right axis. double[][] waveform=getWaveform(pamDetection); @@ -107,20 +107,23 @@ public abstract class WaveformPlot implements DetectionPl else binLength=waveform[0].length; //System.out.println("Waveform Length: " + binLength); +// plotProjector.setEnableScrollBar(true); + //set the scroller minimum and maximum plotProjector.setMinScrollLimit(0); plotProjector.setMaxScrollLimit((binLength*1000.)/sR); - plotProjector.setEnableScrollBar(true); plotProjector.setScrollAxis(Side.BOTTOM); -// System.out.println("Waveform Time Axis: min " + (plotProjector.getAxis(Side.BOTTOM).getMinVal()/1000.)*sR + -// " max: " + (plotProjector.getAxis(Side.BOTTOM).getMaxVal()/1000)*sR); - //set the min and max value. + //set the min and max value from the bottom axis on the top axis. This essentially means that the scroller can + //set the top axis. plotProjector.setAxisMinMax((plotProjector.getAxis(Side.BOTTOM).getMinVal()/1000.)*sR, (plotProjector.getAxis(Side.BOTTOM).getMaxVal()/1000)*sR, Side.TOP); + System.out.println("WaveformPlot.setupAxis: min " + (plotProjector.getAxis(Side.BOTTOM).getMinVal()/1000.)*sR + + " max: " + (plotProjector.getAxis(Side.BOTTOM).getMaxVal()/1000)*sR); + // plotProjector.setAxisMinMax(0, (binLength*1000.)/sR, Side.BOTTOM); plotProjector.setAxisMinMax(-1, 1, Side.LEFT); //TODO @@ -182,6 +185,7 @@ public abstract class WaveformPlot implements DetectionPl * @param projector - the projector */ private void forcePaintPlot(D pamDetection, GraphicsContext gc, Rectangle rectangle, DetectionPlotProjector projector){ + System.out.println("WaveformPlot.forcePaintPlot:"); currentWaveform=getWaveform(pamDetection); if (currentWaveform==null) return; @@ -255,6 +259,7 @@ public abstract class WaveformPlot implements DetectionPl synchronized (storedWaveformLock) { if (currentDetection == null || waveform==null || waveform.length==0 || waveform[0]==null) return; + paintWaveform(waveform, currentDetection.getSequenceBitmap(), g, clipRect, (int) projector.getAxis(Side.TOP).getMinVal(), (int) projector.getAxis(Side.TOP).getMaxVal(), log2Amplitude, color, !waveformPlotParams.showSperateWaveform || waveform.length==1, waveformPlotParams.invert); } @@ -274,7 +279,7 @@ public abstract class WaveformPlot implements DetectionPl */ public static void paintWaveform(double[][] waveform, int channelBitMap, GraphicsContext g, Rectangle clipRect, int minbin, int maxbin, double yScaleInfo, Color color, boolean singlePlot, boolean invert) { - //System.out.println("Paint the waveform: " + clipRect.getWidth()); + System.out.println("Paint the waveform: " + waveform[0].length); g.setLineWidth(1); // boolean singlePlot=!waveformPlotParams.showSperateWaveform; diff --git a/src/detectionPlotFX/rawDDPlot/RawWaveformPlot.java b/src/detectionPlotFX/rawDDPlot/RawWaveformPlot.java index 4deef0f3..8e00f9a8 100644 --- a/src/detectionPlotFX/rawDDPlot/RawWaveformPlot.java +++ b/src/detectionPlotFX/rawDDPlot/RawWaveformPlot.java @@ -7,7 +7,7 @@ import detectionPlotFX.layout.DetectionPlotDisplay; import detectionPlotFX.plots.WaveformPlot; /** - * Plot a click waveform. + * Plot for any RawDataHolder to show a waveform. * @author Jamie Macaulay * */ diff --git a/src/export/ExportOptions.java b/src/export/ExportOptions.java index 0ce6a1f8..2b01f7b3 100644 --- a/src/export/ExportOptions.java +++ b/src/export/ExportOptions.java @@ -7,7 +7,6 @@ import PamController.PamControlledUnitSettings; import PamController.PamSettingManager; import PamController.PamSettings; import PamController.StorageParameters; -import export.layoutFX.ExportParams; import export.swing.ExportProcessDialog; /** @@ -20,10 +19,10 @@ public class ExportOptions implements PamSettings { private static ExportOptions singleInstance; - /** - * Parameters for the exporter. - */ - private ExportParams storageParameters = new ExportParams(); +// /** +// * Parameters for the exporter. +// */ +// private ExportParams storageParameters = new ExportParams(); /** * Swing dialog for exporting data. @@ -57,7 +56,7 @@ public class ExportOptions implements PamSettings { if (exportProcessDialog==null) { exportProcessDialog= new ExportProcessDialog(exportManager); } - this.exportProcessDialog.showOfflineDialog(parentFrame, this.storageParameters); + this.exportProcessDialog.showOfflineDialog(parentFrame, exportManager.getExportParams()); // ExportParams newParams = StorageOptionsDialog.showDialog(parentFrame, storageParameters); // if (newParams != null) { @@ -73,7 +72,7 @@ public class ExportOptions implements PamSettings { @Override public Serializable getSettingsReference() { - return storageParameters; + return exportManager.getExportParams(); } @Override @@ -94,20 +93,21 @@ public class ExportOptions implements PamSettings { @Override public boolean restoreSettings( PamControlledUnitSettings pamControlledUnitSettings) { - storageParameters = ((ExportParams) pamControlledUnitSettings.getSettings()).clone(); + ExportParams storageParameters = ((ExportParams) pamControlledUnitSettings.getSettings()).clone(); + exportManager.setExportParams(storageParameters); return true; } - public void setStorageParameters(ExportParams storageParameters) { - this.storageParameters = storageParameters; + public void setExportParameters(ExportParams storageParameters) { + exportManager.setExportParams(storageParameters); } /** * Get storage parameters settings. * @return the storage paramters settings */ - public ExportParams getStorageParameters() { - return storageParameters; + public ExportParams getExportParameters() { + return exportManager.getExportParams(); } } \ No newline at end of file diff --git a/src/export/layoutFX/ExportParams.java b/src/export/ExportParams.java similarity index 96% rename from src/export/layoutFX/ExportParams.java rename to src/export/ExportParams.java index a4c5dbdc..c9ffd519 100644 --- a/src/export/layoutFX/ExportParams.java +++ b/src/export/ExportParams.java @@ -1,4 +1,4 @@ -package export.layoutFX; +package export; import java.io.Serializable; diff --git a/src/export/MLExport/MLDataUnitExport.java b/src/export/MLExport/MLDataUnitExport.java index 8c67bbaa..4522e269 100644 --- a/src/export/MLExport/MLDataUnitExport.java +++ b/src/export/MLExport/MLDataUnitExport.java @@ -48,8 +48,12 @@ public abstract class MLDataUnitExport> { //UID for the detection. Matrix UID = Mat5.newScalar(dataUnit.getUID()); - //the start sample. - Matrix startSample = Mat5.newScalar(dataUnit.getStartSample()); + Matrix startSample = Mat5.newScalar(0); + if (dataUnit.getStartSample()!=null) { + //the start sample. + startSample = Mat5.newScalar(dataUnit.getStartSample()); + } + //the duration of the detection in samples. Matrix sampleDuration = Mat5.newScalar(dataUnit.getSampleDuration()); diff --git a/src/export/MLExport/MLWhistleMoanExport.java b/src/export/MLExport/MLWhistleMoanExport.java index cdaba823..72e59e08 100644 --- a/src/export/MLExport/MLWhistleMoanExport.java +++ b/src/export/MLExport/MLWhistleMoanExport.java @@ -47,10 +47,10 @@ public class MLWhistleMoanExport extends MLDataUnitExport> { rData.add("UID", dataUnit.getUID()); rData.add("startSample", dataUnit.getStartSample()); rData.add("sampleDuration", dataUnit.getSampleDuration()); - rData.add("freqLimits", new DoubleArrayVector(dataUnit.getBasicData().getFrequency())); +// rData.add("freqLimits", new DoubleArrayVector(dataUnit.getBasicData().getFrequency())); + rData.add("minFreq", dataUnit.getBasicData().getFrequency()[0]); + rData.add("maxFreq", dataUnit.getBasicData().getFrequency()[1]); + rData.add("amplitude", dataUnit.getBasicData().getCalculatedAmlitudeDB()); //there may be no delay info if (dataUnit.getBasicData().getTimeDelaysSeconds()!=null && dataUnit.getBasicData().getTimeDelaysSeconds().length>=1){ diff --git a/src/export/RExport/RWhistleExport.java b/src/export/RExport/RWhistleExport.java index d378af8d..013664ad 100644 --- a/src/export/RExport/RWhistleExport.java +++ b/src/export/RExport/RWhistleExport.java @@ -1,8 +1,14 @@ package export.RExport; +import org.renjin.sexp.AttributeMap; +import org.renjin.sexp.IntArrayVector; +import org.renjin.sexp.ListVector; +import export.MLExport.MLWhistleMoanExport; import org.renjin.sexp.ListVector.NamedBuilder; +import PamUtils.PamArrayUtils; import whistlesAndMoans.ConnectedRegionDataUnit; +import whistlesAndMoans.SliceData; /*** * Export whisltes to RData @@ -13,8 +19,27 @@ public class RWhistleExport extends RDataUnitExport { @Override public NamedBuilder addDetectionSpecificFields(NamedBuilder rData, ConnectedRegionDataUnit dataUnit, int index) { + + + rData.add("nSlices", dataUnit.getConnectedRegion().getNumSlices()); + + int[][] contourData = MLWhistleMoanExport.calcPeakContourWidths( dataUnit); + + IntArrayVector contours = new IntArrayVector(contourData[0]); + IntArrayVector contourWidth = new IntArrayVector(contourData[0]); + + //need to generate a slice struct + ListVector.NamedBuilder peakDatas = createSliceStruct( dataUnit); + + rData.add("nSlices", dataUnit.getConnectedRegion().getNumSlices()); + rData.add("sliceData", peakDatas); + rData.add("contour", contours); + rData.add("contWidth", contourWidth); + rData.add("meanWidth", PamArrayUtils.mean(contourData[0])); + + // TODO Auto-generated method stub -// //nSlices int + //nSlices int // MLInt32 nSlices = new MLInt32(null, new Integer[]{dataUnit.getConnectedRegion().getNumSlices()}, 1); // // //list of structures: sliceNumber int, nPeaks int, peakData @@ -42,6 +67,69 @@ public class RWhistleExport extends RDataUnitExport { return rData; } + + + /** + * Create array of slice structures for output. + * @param dataUnit + * @return + */ + private ListVector.NamedBuilder createSliceStruct(ConnectedRegionDataUnit dataUnit){ + +// Struct mlStructure= new MLStructure("sliceData", new int[]{dataUnit.getConnectedRegion().getSliceData().size(), 1}); + + ListVector.NamedBuilder peakDatas = new ListVector.NamedBuilder(); ; + + + //the start sample. + int sliceNumber; + int nPeaks; + int[][] peakData; + SliceData sliceData; + ListVector.NamedBuilder rData; + for (int i=0; i getUnitClass() { return ConnectedRegionDataUnit.class; diff --git a/src/export/exporter_help.md b/src/export/exporter_help.md new file mode 100644 index 00000000..df85fc41 --- /dev/null +++ b/src/export/exporter_help.md @@ -0,0 +1,94 @@ +# PAMGuard exporter +## Introduction + +The PAMGuard exporter allows users to export PAMGuard data, such as detections, to a variety of different formats. The exporter is a convenient solution for exporting sections or large chunks of a PAMGuard datasets without requiring any code. For more bespoke data management please see the [PAMGuard-MATLAB](https://github.com/PAMGuard/PAMGuardMatlab) library and [PAMBinaries package](https://github.com/TaikiSan21/PamBinaries) which can be used for more bespoke data management. Note that the exporter only exports a sub set of data types - this will expand in future releases. + +## Exporting +The PAMGuard exporter can be accessed from *File->Export*. This brings up the Export dialog. The export dialog allows users to select which data to export, where to export it and the file format to export as. Each data block also has a settings icon which opens the data block's unique data selector. So for example, users can export only specific types of clicks or whistles between certain frequencies. + +

+ +

+ +
Diagram of the exporter dialog. The dialog allows users to select which part of the dataset to export, how to export it and which type of data to export
+ +The main parts of the dialog are as follows. +### Data Options +Select which part of the dataset to export +- Loaded Data : the data currently loaded into memory i.e. usually what you can see in the displays - may be different time oeriods depending on the data type. +- All data : the entire dataset. +- Select data : manually enter a period between two times. +- Specify time chunks : import a csv file with a list of time chunks. + +### Export Options +Select where to export the data to using _Browse..._ and select the maximum allowed file size using the _Maximum file size_ selector. Select the format by toggling one of the data format buttons. Hover over each button to see more info. + +### Export Data +Select which data to export. If a data type has a cog icon next to it then it has a data selector. The data selector settings can be used to filter which detections are exported. For example you may wish only to export clicks of a certain type or perhaps deep learning detections with a prediction value above a certain threshold. Each data selector is unique to the type of data. Note that the exporter only exports a sub set of data types - this will expand in future. + +### Progress +Once _Start_ is selected then the progress bars show progress in exporting the selected data. + +## Export formats +Currently the exporter has three possible output formats. + +### MAT files +MAT files are files which can be opened easily in MATLAB and Python. They can store multiple different data formats e.g. tables, arrays, structures. Each PAMGuard detection is saved as a single structure and then the file contains an array of these structures for each data type. The fields within the structure contains the relevant data unique to each data unit. Whilst data units have unique fields depending on their type e.g. a click or a whistle, there are some fields that are shared between almost all data units - an example of a click detection structure is shown below + +*General fields shared by most data units in PAMGuard* +- *millis*: the unix*1000 start time of the click, whistle, clip etc. in milliseconds; this number can be converted to a date/time with millisecond accuracy. +- *date*: the start time of the click in MATLAB datenum format. Use datastr(date) to show a time string. +- *UID*: a unique serial number for the detection. Within a processed dataset no other detection will have this number. +- *startSample*: The first sample of this detection - often used for finer scale time delay measurements. Samples refers to the number of samples in total the sound card has taken since processing begun or a new file has been created. +- *channelMap*: The channel map for this detection. One number which represents which channels this detection is from: To get the true channels use the getChannels(channelMap) function. + +*Unique to clicks* +- *triggerMap*: which channel triggered the detection. +- *type*: Classification type. Must use database or settings to see what species this refers to. +- *duration*: Duration of this click detection in samples. +- *nChan*: Number of channels the detection was made on. +- *wave*: Waveform data for each channel. + +Note that the format of each struct is the same as the format if extracting data using the [PAMGuard-MATLAB](https://github.com/PAMGuard/PAMGuardMatlab) library. + +To open an exported .mat file simply drag it into **MATLAB** or use the function; +```Matlab + load(/my/path/to/file.mat) +``` + +To open a .mat file in **Python** use + +```Python +import scipy.io +mat = scipy.io.loadmat('/my/path/to/file.mat') +clkstruct = mat['det_20170704_204536_580'] #The name of the struct array within the file + +#Extract the third waveform from a click example +nwaves = len(clkstruct[0]) #Number of clicks +thirdwaveform = clkstruct[0, 2]['wave'] #Waveform from third click in samples between -1 and 1. +``` + +### R +Data can be exported to an RData frame. The data are exported as R structs with the same fields as in MATLAB (and PAMBinaries package). To open a an RData frame open RStudio and import the file or use; + +```R +load("/my/path/to/file.RData") +``` + +### Wav files +Any detection which contains raw sound data, for example a click, clip or deep learning detection, can be exported as a wav file. When wav files are selected three options are presented for saving files. + +

+ +

+ +
When wav files are selected additional options are presented on how to save the file
+ +- *Zero pad* : Here detections are saved as wav files with the time in between detections zero padded. The resulting files will be as large as the initial wav files processed to create the data. This can be useful if for example opening the files in another acoustic analysis program. + +- *Concatenate* : The detections are saved to a wav file without any zero padding. This saves storage space but temporal information is lost within the wav file. The sample positions of each detection are saved in a text file along with the wav file so that temporal info is available if needed. This is same format as SoundTrap click detection data. + +- *Individual* : Each detection is saved in it's own time stamped individual sound file. + +## After export +Once data are exported, the exported files are not part of PAMGuard's data management system i.e. PAMGuard has no record they exist and they are not shown in the data model etc. If you export the same data again to the same location, then previous exported files may be overwritten without warning. \ No newline at end of file diff --git a/src/export/layoutFX/ExporterPane.java b/src/export/layoutFX/ExporterPane.java index 5c923a61..c55534cf 100644 --- a/src/export/layoutFX/ExporterPane.java +++ b/src/export/layoutFX/ExporterPane.java @@ -1,6 +1,7 @@ package export.layoutFX; import PamController.SettingsPane; +import export.ExportParams; import javafx.scene.Node; import javafx.scene.control.Label; import pamViewFX.fxNodes.PamBorderPane; diff --git a/src/export/resources/PAMGuard_exporter_dialog.png b/src/export/resources/PAMGuard_exporter_dialog.png new file mode 100644 index 00000000..8ce97115 Binary files /dev/null and b/src/export/resources/PAMGuard_exporter_dialog.png differ diff --git a/src/export/resources/PAMGuard_exporter_dialog_annotated.png b/src/export/resources/PAMGuard_exporter_dialog_annotated.png new file mode 100644 index 00000000..2a27c8af Binary files /dev/null and b/src/export/resources/PAMGuard_exporter_dialog_annotated.png differ diff --git a/src/export/resources/PAMGuard_exporter_dialog_annotated.pptx b/src/export/resources/PAMGuard_exporter_dialog_annotated.pptx new file mode 100644 index 00000000..87c8d4b9 Binary files /dev/null and b/src/export/resources/PAMGuard_exporter_dialog_annotated.pptx differ diff --git a/src/export/resources/PAMGuard_exporter_dialog_wav.png b/src/export/resources/PAMGuard_exporter_dialog_wav.png new file mode 100644 index 00000000..72299302 Binary files /dev/null and b/src/export/resources/PAMGuard_exporter_dialog_wav.png differ diff --git a/src/export/wavExport/WavFileExportManager.java b/src/export/wavExport/WavDetExport.java similarity index 100% rename from src/export/wavExport/WavFileExportManager.java rename to src/export/wavExport/WavDetExport.java diff --git a/src/export/wavExport/WavDetExportManager.java b/src/export/wavExport/WavDetExportManager.java new file mode 100644 index 00000000..b6f8f9c3 --- /dev/null +++ b/src/export/wavExport/WavDetExportManager.java @@ -0,0 +1,252 @@ +package export.wavExport; + +import java.awt.Component; +import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; + +import javax.sound.sampled.AudioFormat; +import javax.swing.filechooser.FileSystemView; + +import PamController.PamController; +import PamDetection.RawDataUnit; +import PamUtils.PamCalendar; +import PamUtils.PamUtils; +import PamView.paneloverlay.overlaymark.OverlayMark; +import PamguardMVC.LoadObserver; +import PamguardMVC.PamDataUnit; +import PamguardMVC.PamObservable; +import PamguardMVC.PamObserver; +import PamguardMVC.PamObserverAdapter; +import PamguardMVC.PamRawDataBlock; +import PamguardMVC.RawDataHolder; +import PamguardMVC.dataOffline.OfflineDataLoading; +import dataMap.OfflineDataMapPoint; +import detectiongrouplocaliser.DetectionGroupSummary; +import export.PamDataUnitExporter; +import javafx.scene.layout.Pane; +import wavFiles.Wav16AudioFormat; +import wavFiles.WavFileWriter; + +/** + * Writes data units and/or ordered raw data to a wav file. Has functions to + * handle .wav file writing based on overlay marks and detection groups with + * functions to make decisions based on what type of data unit is selected and + * whether raw data is available. + *

+ * There are two primary use cases;
+ * 1) Order raw data from an overlay mark and save as a wav file
+ * 2) Save a list of data units to wav files - either a single file with zero + * pads, a concatenated file or separate files. + * + * @author Jamie Macaulay + * + */ +public class WavDetExportManager implements PamDataUnitExporter { + + /** + * Options for exporting wav files. + */ + private WavExportOptions wavFileoptions = new WavExportOptions(); + + /** + * Settings panel for wav file exporting + */ + private WavOptionsPanel wavOptionsPanel; + + /** + * Exporter of wav files. + */ + private WavDetExport wavDetExport = new WavDetExport(); + + private File currentFile; + + public WavDetExportManager() { + + } + + @Override + public boolean hasCompatibleUnits(Class dataUnitType) { + // boolean implementsInterface = Arrays.stream(dataUnitType.getInterfaces()).anyMatch(i -> i == RawDataHolder.class); + if ( RawDataHolder.class.isAssignableFrom(dataUnitType)) return true; + return false; + } + + + + @Override + public boolean exportData(File fileName, + List dataUnits, boolean append) { + + if (fileName==null) return false; + + + if (this.currentFile==null || this.currentFile.compareTo(fileName)!=0) { + //we have a new .wav file to create. + if (fileName.exists()) { + //we need to delete it + System.out.println("PAMGuard export: wav file already existed and has been deleted: " + fileName.getName()); + fileName.delete(); + } + } + + this.currentFile = fileName; + + + //make sure we have the latest options. + if (wavOptionsPanel!=null) { + //means the options panel has been opened. + wavFileoptions = wavOptionsPanel.getParams(wavFileoptions); + } + + //should we zeropad? + //saveDataUnitWav(dataUnits); + switch (wavFileoptions.wavSaveChoice) { + case WavExportOptions.SAVEWAV_CONCAT: + wavDetExport.writeDataUnitWav(dataUnits, fileName, false); + break; + case WavExportOptions.SAVEWAV_INDIVIDUAL: + //here the filename will not be used but the parent folder will be used instead to write + //lots of wav files to. + wavDetExport.writeDataUnitWavs(dataUnits, fileName); + break; + case WavExportOptions.SAVEWAV_ZERO_PAD: + wavDetExport.writeDataUnitWav(dataUnits, fileName, true); + break; + } + + return true; + } + + + + @Override + public String getFileExtension() { + return "wav"; + } + + + + @Override + public String getIconString() { + return "mdi2f-file-music"; + } + + + + @Override + public String getName() { + return "raw sound"; + } + + + + @Override + public void close() { + // TODO Auto-generated method stub + } + + + + @Override + public boolean isNeedsNewFile() { + return false; + } + + + + @Override + public Component getOptionsPanel() { + if (this.wavOptionsPanel==null) { + this.wavOptionsPanel = new WavOptionsPanel(); + } + wavOptionsPanel.setParams(this.wavFileoptions) ; + return wavOptionsPanel; + } + + + + @Override + public Pane getOptionsPane() { + // TODO - make FX version of settings. + return null; + } + + @Override + public void prepareExport() { + this.currentFile = null; + + } + + + + + + // hello(){ + // + // + // if (mark==null) { + // start= foundDataUnits.getFirstTimeMillis(); + // end= foundDataUnits.getLastTimeMillis(); + // } + // + // File folder = new File(currentFolder); + // + // //save a .wav file clip. + // if (!folder.exists()){ + // if (!folder.mkdir()){ + // //TODO- warning message. + // return; + // } + // } + // + // String currentPath = PamCalendar.formatFileDateTime(); + // //add data types to the filen,ae + // for (int i=0 ;i" + + "Concatonate detections within wav files. If selected, then the wav files are concatenated" + + "
" + + "and a seperate text file encodes the detection times - this saves a lot of storage space!" + + ""); + + zeroPad.setToolTipText( + "" + + "Zero pad wav files. If selected, then the wav files are zero padding between detections " + + "
" + + "so they appear at the right time - this can be very storage space intensive. " + + ""); + + indvidualWav.setToolTipText( + "" + + "Save each detection as an individual time stamped wav file" + + ""); + + } + + public void setParams(WavExportOptions wavExportOptions) { + switch (wavExportOptions.wavSaveChoice) { + + case WavExportOptions.SAVEWAV_CONCAT: + noZeroPad.setSelected(true); + break; + case WavExportOptions.SAVEWAV_ZERO_PAD: + zeroPad.setSelected(true); + break; + case WavExportOptions.SAVEWAV_INDIVIDUAL: + indvidualWav.setSelected(true); + break; + + } + + } + + public WavExportOptions getParams(WavExportOptions wavExportOptions) { + + if (zeroPad.isSelected()) wavExportOptions.wavSaveChoice = WavExportOptions.SAVEWAV_ZERO_PAD; + if (noZeroPad.isSelected()) wavExportOptions.wavSaveChoice = WavExportOptions.SAVEWAV_CONCAT; + if (indvidualWav.isSelected()) wavExportOptions.wavSaveChoice = WavExportOptions.SAVEWAV_INDIVIDUAL; + + return wavExportOptions; + } + +} diff --git a/src/offlineProcessing/OLProcessDialog.java b/src/offlineProcessing/OLProcessDialog.java index 79601623..872e0e20 100644 --- a/src/offlineProcessing/OLProcessDialog.java +++ b/src/offlineProcessing/OLProcessDialog.java @@ -2,6 +2,7 @@ package offlineProcessing; import java.awt.BorderLayout; import java.awt.Color; +import java.awt.Container; import java.awt.GridBagConstraints; import java.awt.GridBagLayout; import java.awt.Insets; @@ -63,6 +64,7 @@ public class OLProcessDialog extends PamDialog { private JButton[] settingsButton; private JLabel status, currFile; private JProgressBar globalProgress; // file by file progress 1: nFiles + private JProgressBar loadedProgress; // progress throgh loaded data private JCheckBox deleteOldData; private JLabel dataInfo; @@ -111,6 +113,10 @@ public class OLProcessDialog extends PamDialog { */ private boolean isNeedaNote = true; + /** + * Tasks panel + */ + private PamAlignmentPanel tasksPanel; public OLProcessDialog(Window parentFrame, OfflineTaskGroup taskGroup, String title) { super(parentFrame, title, false); @@ -149,7 +155,7 @@ public class OLProcessDialog extends PamDialog { dataSelectPanel.add(BorderLayout.SOUTH, southPanel); - JPanel tasksPanel = new PamAlignmentPanel(BorderLayout.WEST); + tasksPanel = new PamAlignmentPanel(BorderLayout.WEST); tasksPanel.setLayout(new GridBagLayout()); tasksPanel.setBorder(new TitledBorder("Tasks")); int nTasks = taskGroup.getNTasks(); @@ -723,7 +729,7 @@ public class OLProcessDialog extends PamDialog { * @author Doug Gillespie * */ - class OLMonitor implements TaskMonitor { + public class OLMonitor implements TaskMonitor { @Override public void setTaskStatus(TaskMonitorData taskMonitorData) { @@ -734,6 +740,7 @@ public class OLProcessDialog extends PamDialog { else { currFile.setText(taskMonitorData.fileOrStatus); } + switch (taskMonitorData.taskActivity) { case LINKING: case LOADING: @@ -755,6 +762,7 @@ public class OLProcessDialog extends PamDialog { default: break; } + switch (taskMonitorData.taskStatus) { case COMPLETE: globalProgress.setValue(100); @@ -878,6 +886,17 @@ public class OLProcessDialog extends PamDialog { public void setNeedaNote(boolean isNeedaNote) { this.isNeedaNote = isNeedaNote; } + + public JProgressBar getGlobalProgress() { + return globalProgress; + } + + + public PamAlignmentPanel getTasksPanel() { + return tasksPanel; + } + + diff --git a/src/pamViewFX/PamLauncherFXApp.java b/src/pamViewFX/PamLauncherFXApp.java new file mode 100644 index 00000000..513590bf --- /dev/null +++ b/src/pamViewFX/PamLauncherFXApp.java @@ -0,0 +1,24 @@ +package pamViewFX; + +import javafx.application.Application; +import javafx.scene.Scene; +import javafx.scene.layout.StackPane; +import javafx.stage.Stage; + +public class PamLauncherFXApp extends Application { + + public static void main(String[] args) { + launch(args); + } + + @Override + public void start(Stage primaryStage) { + primaryStage.setTitle("PAMGuard Launcher"); + + + StackPane root = new StackPane(); + root.getChildren().add(new PamLauncherPane()); + primaryStage.setScene(new Scene(root, 500, 250)); + primaryStage.show(); + } +} diff --git a/src/pamViewFX/PamLauncherFXAppLauncher.java b/src/pamViewFX/PamLauncherFXAppLauncher.java new file mode 100644 index 00000000..142dbb86 --- /dev/null +++ b/src/pamViewFX/PamLauncherFXAppLauncher.java @@ -0,0 +1,9 @@ +package pamViewFX; + +public class PamLauncherFXAppLauncher { + + public static void main(String[] args) { + PamLauncherFXApp.main(args); + } + +} diff --git a/src/pamViewFX/PamLauncherPane.java b/src/pamViewFX/PamLauncherPane.java new file mode 100644 index 00000000..622d0183 --- /dev/null +++ b/src/pamViewFX/PamLauncherPane.java @@ -0,0 +1,106 @@ +package pamViewFX; + +import java.net.MalformedURLException; +import java.nio.file.Path; +import java.nio.file.Paths; + +import javafx.geometry.Insets; +import javafx.geometry.Pos; +import javafx.scene.control.ContentDisplay; +import javafx.scene.image.Image; +import javafx.scene.image.ImageView; +import javafx.scene.layout.BorderPane; +import javafx.scene.layout.HBox; +import pamViewFX.fxNodes.PamBorderPane; +import pamViewFX.fxNodes.PamButton; +import pamViewFX.fxNodes.utilityPanes.PamToggleSwitch; +import pamViewFX.fxStyles.PamAtlantaStyle; +import pamViewFX.fxStyles.PamDefaultStyle; +import pamViewFX.fxStyles.PamStylesManagerFX; + +/** + * Shows a launcher pane which allows a user to open a real time or viewer configuration. + */ +public class PamLauncherPane extends PamBorderPane { + + public static final double BUTTON_SIZE = 120; + + public PamLauncherPane() { + + PamButton buttonNormal = new PamButton("Real time"); + buttonNormal.setPrefSize(BUTTON_SIZE, BUTTON_SIZE); + PamButton buttonViewer = new PamButton("Post processing"); + buttonViewer.setPrefSize(BUTTON_SIZE, BUTTON_SIZE); + + Path pathNormal = Paths.get("./src/Resources/pamguardIcon.png"); + Path pathViewer = Paths.get("./src/Resources/pamguardIconV.png"); + + Image img; + + //create the normal mode button + try { + img = new Image(pathNormal.toUri().toURL().toExternalForm()); + ImageView view = new ImageView(img); + buttonNormal.setGraphic(view); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + buttonNormal.setContentDisplay(ContentDisplay.TOP); + + //create the viewer mode button + try { + img = new Image(pathViewer.toUri().toURL().toExternalForm()); + ImageView view = new ImageView(img); + buttonViewer.setGraphic(view); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + buttonViewer.setContentDisplay(ContentDisplay.TOP); + + HBox butttonBox = new HBox(); + butttonBox.setSpacing(40); + butttonBox.setPadding(new Insets(40,40,40,40)); + + butttonBox.getChildren().addAll(buttonNormal, buttonViewer); + + + PamToggleSwitch newVersionSwitch = new PamToggleSwitch("PAMGuardFX"); + newVersionSwitch.selectedProperty().addListener((obsVal, oldVal, newVal)->{ + this.getStylesheets().clear(); + if (newVal) { + //this.getStylesheets().add(new PrimerDark().getUserAgentStylesheet()); +// Platform.runLater(()->{ +// Application.setUserAgentStylesheet(new PrimerDark().getUserAgentStylesheet()); +// }); + PamStylesManagerFX.getPamStylesManagerFX().setCurStyle(new PamAtlantaStyle()); + this.getStylesheets().addAll(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getDialogCSS()); + this.setStyle("-fx-background-color: -fx-darkbackground"); + } + }); + + BorderPane.setAlignment(newVersionSwitch, Pos.CENTER_RIGHT); + newVersionSwitch.setAlignment(Pos.CENTER_RIGHT); + this.setTop(newVersionSwitch); + this.setCenter(butttonBox); + + this.setPadding(new Insets(5,5,5,5)); + + +// Application.setUserAgentStylesheet(new PrimerDark().getUserAgentStylesheet()); +// this.getStylesheets().add(getClass().getResource(primerPAMGuard).toExternalForm()); +// this.getStylesheets().add(getClass().getResource(new PrimerDark().getUserAgentStylesheet()).toExternalForm()); + PamStylesManagerFX.getPamStylesManagerFX().setCurStyle(new PamDefaultStyle()); + } + + private void setStyle(boolean PAMGuardFX) { + this.getStylesheets().clear(); + + if (PAMGuardFX) { + PamStylesManagerFX.getPamStylesManagerFX().setCurStyle(new PamAtlantaStyle()); + this.getStylesheets().addAll(PamStylesManagerFX.getPamStylesManagerFX().getCurStyle().getDialogCSS()); + this.setStyle("-fx-background-color: -fx-darkbackground"); + } + + } + +} diff --git a/src/pamViewFX/PamSettingsMenuPane.java b/src/pamViewFX/PamSettingsMenuPane.java index 545c8b00..f7ed6271 100644 --- a/src/pamViewFX/PamSettingsMenuPane.java +++ b/src/pamViewFX/PamSettingsMenuPane.java @@ -2,6 +2,7 @@ package pamViewFX; import generalDatabase.DBControlUnit; +import java.io.File; import java.util.Optional; import binaryFileStorage.BinaryStore; @@ -16,7 +17,8 @@ import PamController.StorageParameters; import PamController.soundMedium.GlobalMedium; import PamController.soundMedium.GlobalMedium.SoundMedium; import PamModel.PamModuleInfo; -import PamView.dialog.warn.WarnOnce; +import PamUtils.PamFileFilter; +import pamViewFX.fxGlyphs.PamGlyphDude; import pamViewFX.fxNodes.PamButton; import pamViewFX.fxNodes.PamVBox; import pamViewFX.fxNodes.PamHBox; @@ -34,6 +36,8 @@ import javafx.scene.control.MenuButton; import javafx.scene.control.MenuItem; import javafx.scene.control.Separator; import javafx.scene.text.TextAlignment; +import javafx.stage.FileChooser; +import javafx.stage.FileChooser.ExtensionFilter; import javafx.scene.control.ToggleButton; import javafx.scene.control.ToggleGroup; import javafx.scene.control.Tooltip; @@ -81,13 +85,16 @@ public class PamSettingsMenuPane extends PamVBox { PamSettingManager.getInstance().saveSettings(null); }); styleButton(saveConfig); + saveConfig.setGraphic(PamGlyphDude.createPamIcon("mdi2c-content-save-outline", + PamGuiManagerFX.iconSize)); PamButton saveConfigAs=new PamButton("Save as..."); saveConfigAs.setOnAction((action)->{ - PamSettingManager.getInstance().saveSettingsAs(null); + saveSettingsAs(); }); styleButton(saveConfigAs); - + saveConfigAs.setGraphic(PamGlyphDude.createPamIcon("mdi2c-content-save-move-outline", + PamGuiManagerFX.iconSize)); //Air or water mode ToggleButton toggleButton1 = new ToggleButton("Water"); @@ -97,6 +104,7 @@ public class PamSettingsMenuPane extends PamVBox { if (PamController.getInstance().getGlobalMediumManager().getGlobalMediumParameters().currentMedium==SoundMedium.Water) return; //do nothing. PamController.getInstance().getGlobalMediumManager().setCurrentMedium(SoundMedium.Water); }); + ToggleButton toggleButton2 = new ToggleButton("Air"); toggleButton2.setPrefWidth(60); @@ -114,6 +122,8 @@ public class PamSettingsMenuPane extends PamVBox { Label mediumLabel = new Label("Sound Medium"); mediumLabel.setAlignment(Pos.CENTER_LEFT); mediumLabel.setPadding(new Insets(0,0,0,15)); + mediumLabel.setGraphic(PamGlyphDude.createPamIcon("mdi2w-waves", + PamGuiManagerFX.iconSize)); //styleButton(mediumLabel); @@ -132,9 +142,13 @@ public class PamSettingsMenuPane extends PamVBox { PamButton generalSettings=new PamButton("General Settings..."); styleButton(generalSettings); + generalSettings.setGraphic(PamGlyphDude.createPamIcon("mdi2c-cog", + PamGuiManagerFX.iconSize)); MenuButton settings=new MenuButton("Module Settings"); settings.setPopupSide(Side.RIGHT); + settings.setGraphic(PamGlyphDude.createPamIcon("mdi2c-cogs", + PamGuiManagerFX.iconSize)); // settings.setStyle("-fx-background-radius: 0;" // + " -fx-border-color: transparent; -fx-padding: 0 0 0 0;"); @@ -173,6 +187,8 @@ public class PamSettingsMenuPane extends PamVBox { } }); styleButton(database); + database.setGraphic(PamGlyphDude.createPamIcon("mdi2d-database", + PamGuiManagerFX.iconSize)); PamButton binaryStorage=new PamButton("Binary Storage..."); binaryStorage.setOnAction((action)->{ @@ -193,6 +209,8 @@ public class PamSettingsMenuPane extends PamVBox { } }); styleButton(binaryStorage); + binaryStorage.setGraphic(PamGlyphDude.createPamIcon("mdi2f-file-table", + PamGuiManagerFX.iconSize)); PamButton storageManager=new PamButton("Storage Manager..."); storageManager.setOnAction((action)->{ @@ -204,24 +222,35 @@ public class PamSettingsMenuPane extends PamVBox { } }); styleButton(storageManager); + storageManager.setGraphic(PamGlyphDude.createPamIcon("mdi2d-database-cog", + PamGuiManagerFX.iconSize)); PamButton help=new PamButton("Help..."); styleButton(help); + help.setGraphic(PamGlyphDude.createPamIcon("mdi2h-help-circle-outline", + PamGuiManagerFX.iconSize)); PamButton about=new PamButton("About..."); styleButton(about); - + about.setGraphic(PamGlyphDude.createPamIcon("mdi2i-information-outline", + PamGuiManagerFX.iconSize)); // PamButton tip=new PamButton("Tip of the day..."); // styleButton(tip); PamButton website=new PamButton("Website"); styleButton(website); + website.setGraphic(PamGlyphDude.createPamIcon("mdi2e-earth", + PamGuiManagerFX.iconSize)); PamButton contact=new PamButton("Found a bug?"); styleButton(contact); + contact.setGraphic(PamGlyphDude.createPamIcon("mdi2b-bug", + PamGuiManagerFX.iconSize)); PamButton checkForUpdates=new PamButton("Check for updates"); styleButton(checkForUpdates); + checkForUpdates.setGraphic(PamGlyphDude.createPamIcon("mdi2r-refresh", + PamGuiManagerFX.iconSize)); this.getChildren().addAll(settingsLabel,saveConfig,saveConfigAs, new Separator(), mediumToggleBox, generalSettings, settings, new Separator(), storageManager, database, binaryStorage, new Separator(), help, checkForUpdates, website, contact, about); @@ -268,4 +297,55 @@ public class PamSettingsMenuPane extends PamVBox { } } + + /** + * Save settings to a new psf file. + * @param frame parent frame for dialog. + */ + public void saveSettingsAs() { + /* + * get a new file name, set that as the current file + * then write all settings to it. + */ + File file = null; + String currentfileName = PamSettingManager.getInstance().getSettingsFileName(); + if (currentfileName != null) { + file =new File(currentfileName); + } + + FileChooser fileChooser = new FileChooser(); + fileChooser.setTitle("Open Resource File"); + fileChooser.getExtensionFilters().addAll( + new ExtensionFilter("PAMGuard settings files", "*.xml", "*.psfx")); + if (currentfileName!=null) { + fileChooser.setInitialDirectory(file); + } + + File selectedFile = fileChooser.showOpenDialog(this.getScene().getWindow()); + + if (selectedFile == null) { + return; + } + + selectedFile = PamFileFilter.checkFileEnd(selectedFile, PamSettingManager.getCurrentSettingsFileEnd(), true); + + + System.out.println("Saving settings to file " + selectedFile.getAbsolutePath()); + + // Insert the new file into the top of the recent psf file list. Also check + // if we are running remotely, which probably means the user double-clicked on + // a psf to start Pamguard. In that case, change the remotePSF pointer to + // the new file as well + PamSettingManager.getInstance().setDefaultFile(selectedFile.getAbsolutePath()); + + if (PamSettingManager.remote_psf != null) { + PamSettingManager.remote_psf = selectedFile.getAbsolutePath(); + } + + PamSettingManager.getInstance().saveSettings(PamSettingManager.SAVE_PSF); + + PamController.getInstance().getGuiFrameManager().sortFrameTitles(); + + } + } diff --git a/src/pamViewFX/fxNodes/pamAxis/PamDateAxis.java b/src/pamViewFX/fxNodes/pamAxis/PamDateAxis.java index e877beec..38261ff3 100644 --- a/src/pamViewFX/fxNodes/pamAxis/PamDateAxis.java +++ b/src/pamViewFX/fxNodes/pamAxis/PamDateAxis.java @@ -33,8 +33,9 @@ public class PamDateAxis extends ValueAxis { /** We use these for auto ranging to pick a user friendly tick unit. (must be increasingly bigger)*/ private static final double[] TICK_UNIT_DEFAULTS = { + 3600000, // 1 hour 86400000, // 1 day - 172800000, // 2 das + 172800000, // 2 days 259200000, // 3 days 345600000, // 4 days 432000000, // 5 days @@ -63,8 +64,9 @@ public class PamDateAxis extends ValueAxis { /** These are matching date formatter strings */ private static final String[] TICK_UNIT_FORMATTER_DEFAULTS = { - "MM/dd/yy", // 1 day - "MM/dd/yy", // 2 das + "HH:mm:SS", // 1 hour + "HH:mm:SS", // 1 day + "MM/dd/yy", // 2 days "MM/dd/yy", // 3 days "MM/dd/yy", // 4 days "MM/dd/yy", // 5 days diff --git a/src/pamViewFX/fxNodes/pamScrollers/acousticScroller/ScrollBarPane.java b/src/pamViewFX/fxNodes/pamScrollers/acousticScroller/ScrollBarPane.java index 36124e0a..f9196cb0 100644 --- a/src/pamViewFX/fxNodes/pamScrollers/acousticScroller/ScrollBarPane.java +++ b/src/pamViewFX/fxNodes/pamScrollers/acousticScroller/ScrollBarPane.java @@ -6,6 +6,8 @@ import javafx.beans.property.BooleanProperty; import javafx.beans.property.DoubleProperty; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.property.SimpleDoubleProperty; +import javafx.beans.value.ChangeListener; +import javafx.beans.value.ObservableValue; import javafx.geometry.Point2D; import javafx.scene.Cursor; import javafx.scene.Node; @@ -256,78 +258,6 @@ public class ScrollBarPane extends PamBorderPane { return canvas; } - /** - * Create the text field that allows to manually chage the visible amount amount property. - */ - private void createTextField(){ - //create the textbox - textBox= new TextField(); - textBox.layoutXProperty().bind(rectangle.layoutXProperty().add(rectangle.widthProperty().divide(2)).subtract(textBox.widthProperty().divide(2))); - textBox.layoutYProperty().bind(rectangle.heightProperty().divide(2).subtract(textBox.heightProperty().divide(2))); - textBox.setOnAction((action)-> { - double millis=this.getTextBoxValue(textBox.getText()); - if (millis<=0 || millis>(this.maxValueProperty.get()-this.minValueProperty.get())){ - textBoxErrorFlash(textBox); - this.setTextBoxValue(visibleAmountProperty.get()); - } - else{ - visibleAmountProperty.setValue(millis); - } - - }); - - ft = new FadeTransition(Duration.millis(3000), textBox); - - textBox.getStyleClass().add("text_field_trans"); - textBox.setPrefWidth(70); - - //the rectangle itself. - textBox.setOnMousePressed((event)->{ - rectanglePressed(event); - }); - - - textBox.setOnMouseReleased((event)->{ - rectangleReleased(event); - - }); - - //text box needs to to drag the rectangle so there isn't a drag 'dead space' - textBox.setOnMouseDragged((event)->{ - rectangleDragged(event); - }); - - textBox.setOnMouseEntered((event)->{ - setTextBoxVisible(true); - }); - - textBox.setOnMouseExited((event)->{ - //only set invisible if not in rectangle. This is for fast mouse movements were the exist of the rectangle may not be called - if (!rectangle.contains(rectangle.sceneToLocal(event.getSceneX(), event.getSceneY()))){ - setTextBoxVisible(false); - } - }); - - setTextBoxVisible(false); - - //show and hide text box so keeps the scroll bar beautiful - rectangle.setOnMouseEntered((event)->{ - setTextBoxVisible(true); - }); - - rectangle.setOnMouseExited((event)->{ - setTextBoxVisible(false); - }); - - //make sure the text box chnages with visible amount., - this.visibleAmountProperty.addListener((obsVal, newVal, oldVal)->{ - setTextBoxValue(visibleAmountProperty.get()); - }); - - - setTextBoxValue(visibleAmountProperty.get()); - } - /** * Create the rectangle which can be dragged to change time but also dragged to change the width of time * shown. @@ -382,6 +312,7 @@ public class ScrollBarPane extends PamBorderPane { rightDrag.getChildren().add(rightdragLine); rectangle.getChildren().add(rightDrag); + rectangle.setCursor(Cursor.OPEN_HAND); //Change cursor to hand //now set behaviours leftDrag.setOnMousePressed((event)->{ @@ -436,15 +367,17 @@ public class ScrollBarPane extends PamBorderPane { }); //now set behaviours - rightDrag.setOnMousePressed((event)->{ + rightDrag.setOnMousePressed((event)->{ leftLayoutX=rectangle.getLayoutX(); isChanging.set(true); + rectangle.setCursor(Cursor.CLOSED_HAND); //Change cursor to hand dragStarted(event, rightDrag); }); rightDrag.setOnMouseReleased((event)->{ currentValueProperty.setValue(calcScrollBarVal(rectangle.getLayoutX())); isChanging.set(false); + rectangle.setCursor(Cursor.OPEN_HAND); //Change cursor to hand dragging(event); }); @@ -496,6 +429,7 @@ public class ScrollBarPane extends PamBorderPane { rectangle.setOnMouseDragged((event)->{ rectangleDragged(event); }); + return rectangle; } @@ -543,6 +477,87 @@ public class ScrollBarPane extends PamBorderPane { } } + /** + * Create the text field that allows to manually chage the visible amount amount property. + */ + private void createTextField(){ + //create the textbox + textBox= new TextField(); + textBox.layoutXProperty().bind(rectangle.layoutXProperty().add(rectangle.widthProperty().divide(2)).subtract(textBox.widthProperty().divide(2))); + textBox.layoutYProperty().bind(rectangle.heightProperty().divide(2).subtract(textBox.heightProperty().divide(2))); + textBox.setOnAction((action)-> { + double millis=this.getTextBoxValue(textBox.getText()); + if (millis<=0 || millis>(this.maxValueProperty.get()-this.minValueProperty.get())){ + textBoxErrorFlash(textBox); + this.setTextBoxValue(visibleAmountProperty.get()); + } + else{ + visibleAmountProperty.setValue(millis); + } + + }); + + ft = new FadeTransition(Duration.millis(3000), textBox); + + textBox.getStyleClass().add("text_field_trans"); + textBox.setPrefWidth(70); + + //the rectangle itself. + textBox.setOnMousePressed((event)->{ + rectanglePressed(event); + }); + + + textBox.setOnMouseReleased((event)->{ + rectangleReleased(event); + + }); + + //text box needs to to drag the rectangle so there isn't a drag 'dead space' + textBox.setOnMouseDragged((event)->{ + rectangleDragged(event); + }); + + textBox.setOnMouseEntered((event)->{ + setTextBoxVisible(true); + }); + + textBox.setOnMouseExited((event)->{ + //only set invisible if not in rectangle. This is for fast mouse movements were the exist of the rectangle may not be called + if (!rectangle.contains(rectangle.sceneToLocal(event.getSceneX(), event.getSceneY()))){ + setTextBoxVisible(false); + } + }); + + setTextBoxVisible(false); + + //show and hide text box so keeps the scroll bar beautiful + rectangle.setOnMouseEntered((event)->{ + setTextBoxVisible(true); + }); + + rectangle.setOnMouseExited((event)->{ + setTextBoxVisible(false); + }); + + //make sure the text box chnages with visible amount., + this.visibleAmountProperty.addListener((obsVal, newVal, oldVal)->{ + setTextBoxValue(visibleAmountProperty.get()); + }); + + + setTextBoxValue(visibleAmountProperty.get()); + } + + + /** + * Get the text box that shows the visible amount + * @return - the text field + */ + public TextField getTextBox() { + return textBox; + } + /** * @@ -844,5 +859,18 @@ public class ScrollBarPane extends PamBorderPane { this.showMillis = showMillis; } + + /** + * Convenience function which adds a change listener to the current value and visible amount prooperty. + * @param val - the change listener to add. + */ + public void addValueListener(ChangeListener val) { + //add listener to visible amount property. + visibleAmountProperty.addListener(val); + + //add listener to current value amount property. + currentValueProperty.addListener(val); + } + } diff --git a/src/pamViewFX/fxStyles/PamAtlantaStyle.java b/src/pamViewFX/fxStyles/PamAtlantaStyle.java index 892558d9..a1aba825 100644 --- a/src/pamViewFX/fxStyles/PamAtlantaStyle.java +++ b/src/pamViewFX/fxStyles/PamAtlantaStyle.java @@ -4,6 +4,8 @@ import java.util.ArrayList; import PamView.ColourScheme; import PamView.PamColors; +import atlantafx.base.theme.CupertinoDark; +import atlantafx.base.theme.CupertinoLight; import atlantafx.base.theme.PrimerDark; import atlantafx.base.theme.PrimerLight; @@ -53,6 +55,7 @@ public class PamAtlantaStyle extends PamDefaultStyle { // private String guiCSS = new NordDark().getUserAgentStylesheet(); // protected String primerlight = "/Resources/css/primer-light.css"; protected String primerlight = new PrimerLight().getUserAgentStylesheet(); +// protected String primerlight = new CupertinoLight().getUserAgentStylesheet(); /** * Relative location of the CSS style sheet to be used for the Pamguard standard @@ -62,6 +65,7 @@ public class PamAtlantaStyle extends PamDefaultStyle { // private String dialogCSS = new PrimerDark().getUserAgentStylesheet(); // protected String primerdark = "/Resources/css/primer-dark.css"; protected String primerdark = new PrimerDark().getUserAgentStylesheet(); +// protected String primerdark = new CupertinoDark().getUserAgentStylesheet(); /** diff --git a/src/pamViewFX/symbol/PeakFreqOptionsPane.java b/src/pamViewFX/symbol/PeakFreqOptionsPane.java index fb59c6b4..06f05c7f 100644 --- a/src/pamViewFX/symbol/PeakFreqOptionsPane.java +++ b/src/pamViewFX/symbol/PeakFreqOptionsPane.java @@ -153,7 +153,7 @@ public class PeakFreqOptionsPane extends StandardSymbolModifierPane { super.setParams(); - //important to have here because the super.setParams set this bak to false. + //important to have here because the super.setParams set this back to false. setParams = true; // StandardSymbolOptions standardSymbolOptions = (StandardSymbolOptions) getSymbolModifier().getSymbolChooser().getSymbolOptions(); diff --git a/src/pamguard/PAMGuard_sqlite.java b/src/pamguard/PAMGuard_sqlite.java deleted file mode 100644 index 7c57b1a7..00000000 --- a/src/pamguard/PAMGuard_sqlite.java +++ /dev/null @@ -1,39 +0,0 @@ -package pamguard; - -import java.sql.Connection; -import java.sql.SQLException; - -import org.sqlite.SQLiteConfig; - -import generalDatabase.sqlite.SqliteSQLTypes; - -public class PAMGuard_sqlite { - - public static void main(String[] args) { - - String dbName = "/Users/jdjm/Desktop/section2_cpod/hyskeir_pamguard.sqlite3"; - /* - * Don't use the driver manager, but open from the built in command in - * SQLiteConfig. This will then correctly set the dateformat of the database. - */ - SQLiteConfig config = new SQLiteConfig(); - config.setSharedCache(true); - config.enableRecursiveTriggers(true); - config.enableLoadExtension(true); - config.setDateClass(SqliteSQLTypes.dateClass.getValue()); - config.setDateStringFormat(SQLiteConfig.DEFAULT_DATE_STRING_FORMAT); - - Connection con = null; - try { - con = config.createConnection("jdbc:sqlite:" + dbName); - con.setAutoCommit(false); - } catch (SQLException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - - System.out.println("Connection: " + con); - - } - -} diff --git a/src/rawDeepLearningClassifier/RawDLParams.java b/src/rawDeepLearningClassifier/RawDLParams.java index abb21daf..cbc0b1d0 100644 --- a/src/rawDeepLearningClassifier/RawDLParams.java +++ b/src/rawDeepLearningClassifier/RawDLParams.java @@ -37,6 +37,13 @@ public class RawDLParams implements Serializable, Cloneable { * Holds channel and grouping information */ public GroupedSourceParameters groupedSourceParams = new GroupedSourceParameters(); + + /** + * True to enable segmentation. If segmentation is disabled then the raw waveform from + * a data unit is passed directly to the model. Note that this is not an option for raw sound + * data. + */ + public boolean enableSegmentation = true; /** * The number of raw samples to send to the classifier. @@ -85,7 +92,7 @@ public class RawDLParams implements Serializable, Cloneable { * different class names. If we change model then the class names may change. * Previously annotated data will then be messed up. But, in a giant dataset * that may be an issue. Perhaps users wish to run a new model on some chunk of - * data without messing up all the other classified detectionS which have used + * data without messing up all the other classified detections which have used * that module. So store the data in binary files? That is super inefficient as * the same string is stored many times. So instead store a short which * identifies the string that sits in this table. Everytime a new model is added diff --git a/src/rawDeepLearningClassifier/dlClassification/delphinID/DelphinIDUtils.java b/src/rawDeepLearningClassifier/dlClassification/delphinID/DelphinIDUtils.java index 2a359e6d..a139fa82 100644 --- a/src/rawDeepLearningClassifier/dlClassification/delphinID/DelphinIDUtils.java +++ b/src/rawDeepLearningClassifier/dlClassification/delphinID/DelphinIDUtils.java @@ -382,6 +382,7 @@ public class DelphinIDUtils { // String modelPath = "/Users/au671271/Library/CloudStorage/Dropbox/PAMGuard_dev/Deep_Learning/delphinID/testencounter415/whistle_model_2/whistle_4s_415.zip"; String modelPath = "D:/Dropbox/PAMGuard_dev/Deep_Learning/delphinID/testencounter415/whistle_model_2/whistle_4s_415.zip"; + //line widths in pixels double[] lineWidths = new double[] {6, 7, 10, 15, 20}; for (double lineWidth:lineWidths) { diff --git a/src/rawDeepLearningClassifier/dlClassification/genericModel/DLModelWorker.java b/src/rawDeepLearningClassifier/dlClassification/genericModel/DLModelWorker.java index 22c87512..2dc367c9 100644 --- a/src/rawDeepLearningClassifier/dlClassification/genericModel/DLModelWorker.java +++ b/src/rawDeepLearningClassifier/dlClassification/genericModel/DLModelWorker.java @@ -53,7 +53,7 @@ public abstract class DLModelWorker { public float[][][] dataUnits2ModelInput(ArrayList dataUnits, float sampleRate, int iChan){ @SuppressWarnings("unchecked") - ArrayList rawDataUnits = ( ArrayList) dataUnits; + ArrayList rawDataUnits = (ArrayList) dataUnits; //the number of chunks. int numChunks = rawDataUnits.size(); diff --git a/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericModelTest.java b/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericModelTest.java index a5b5b4d5..d86320a4 100644 --- a/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericModelTest.java +++ b/src/rawDeepLearningClassifier/dlClassification/genericModel/GenericModelTest.java @@ -1,5 +1,7 @@ package rawDeepLearningClassifier.dlClassification.genericModel; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.ArrayList; import org.jamdev.jdl4pam.genericmodel.GenericModel; @@ -13,6 +15,13 @@ import org.jamdev.jdl4pam.transforms.WaveTransform; import org.jamdev.jdl4pam.utils.DLUtils; import org.jamdev.jpamutils.wavFiles.AudioData; +import PamUtils.PamArrayUtils; +import rawDeepLearningClassifier.segmenter.GroupedRawData; +import us.hebi.matlab.mat.format.Mat5; +import us.hebi.matlab.mat.format.Mat5File; +import us.hebi.matlab.mat.types.Matrix; +import us.hebi.matlab.mat.types.Struct; + public class GenericModelTest { public static void rightWhaleTest() { @@ -22,7 +31,7 @@ public class GenericModelTest { //the audio file to test String wavFilePath = "/Users/au671271/Google Drive/PAMGuard_dev/Deep_Learning/Right_whales_DG/SouthernRightWhale001-v1/sar98_trk3_8000.wav"; - + wavFilePath = "/Users/au671271/Google Drive/PAMGuard_dev/Deep_Learning/Right_whales_DG/SouthernRightWhale001-v1/wav_files_timestamp/PAM_20010327_113000.wav"; wavFilePath = "/Users/au671271/git/PAMGuard_resources/deep_learning/right_whale_tutorial/wav/wav_files_timestamp/PAM_20010327_113200.wav"; @@ -74,11 +83,11 @@ public class GenericModelTest { for (int i=0; i<10; i++) { //long time1 = System.currentTimeMillis(); data = new float[][][] {DLUtils.toFloatArray(((FreqTransform) transform).getSpecTransfrom().getTransformedData())}; - + //data = new float[][][] { DLUtils.makeDummySpectrogram(40, 40)}; - + //System.out.println("data len: " + data.length + " " + data[0].length + " " + data[0][0].length); - + output = genericModel.runModel(data); //long time2 = System.currentTimeMillis(); //System.out.println("Time to run model: " + (time2-time1) + " ms"); @@ -98,9 +107,126 @@ public class GenericModelTest { } catch (Exception e) { e.printStackTrace(); } - } + + + public static void clickDLTest() { + + float SAMPLE_RATE = 500000; + //relative paths to the resource folders. + System.out.println("*****Click classification Deep Learning C*****"); + + //relative paths to the resource folders. + String relModelPath = "./src/test/resources/rawDeepLearningClassifier/Generic/risso_click/uniform_model/saved_model.pb"; + String clicksPath = "./src/test/resources/rawDeepLearningClassifier/Generic/risso_click/clicks.mat"; + + Path path = Paths.get(relModelPath); + + GenericModelWorker genericModelWorker = new GenericModelWorker(); + + GenericModelParams genericModelParams = new GenericModelParams(); + genericModelParams.modelPath = path.toAbsolutePath().normalize().toString(); + + //create the transforms. + ArrayList dlTransformParamsArr = new ArrayList(); + //waveform transforms. + dlTransformParamsArr.add(new SimpleTransformParams(DLTransformType.DECIMATE_SCIPY, 250000.)); + dlTransformParamsArr.add(new SimpleTransformParams(DLTransformType.PEAK_TRIM, 128, 1)); + dlTransformParamsArr.add(new SimpleTransformParams(DLTransformType.NORMALISE_WAV, 0., 1, AudioData.ZSCORE)); + + genericModelParams.dlTransfromParams = dlTransformParamsArr; + genericModelParams.dlTransfroms = DLTransformsFactory.makeDLTransforms((ArrayList)genericModelParams.dlTransfromParams); + + + //create the clicks. + path = Paths.get(clicksPath); + ArrayList clicks = importClicks(path.toAbsolutePath().normalize().toString(), SAMPLE_RATE); + + //prep the model + genericModelWorker.prepModel(genericModelParams, null); + + System.out.println("Model has loaded"); + + ArrayList groupedData = new ArrayList(); + + + float prediction = 0; + + for (int i=0; i(); + groupedData.add(clicks.get(i)); //TODO for loop + +// System.out.println("Waveform input: " + groupedData.get(i).getRawData().length + " " + groupedData.get(i).getRawData()[0].length + " " + + groupedData.get(i).getRawData()[0][0]); + + //RUN THE RAW MODEL +// System.out.println("Min max before: "); +// PamArrayUtils.printArray(PamArrayUtils.minmax(groupedData.get(i).getRawData()[0])); + +// double[] wav = PamArrayUtils.normalise(groupedData.get(i).getRawData()[0]); +// +// System.out.println("Min max: "); +// PamArrayUtils.printArray(PamArrayUtils.minmax(wav)); +// float[][] input1 = new float[][] {PamArrayUtils.double2Float(wav)}; +// float[] output1 = genericModelWorker.getModel().runModel(input1); +// System.out.println("Output1: " ); +// PamArrayUtils.printArray(output1); + + //RUN THROUGH THE GENERIC MODEL CLASSIIFER. + ArrayList genericPrediction = genericModelWorker.runModel(groupedData,SAMPLE_RATE, 0); + + float[] output = genericPrediction.get(0).getPrediction(); + + System.out.println(String.format("Click %d Predicted output: %.6f true output: %.6f passed: %b", clicks.get(i).getUID(), + output[0], prediction, output[0]>prediction*0.9 && output[0] importClicks(String filePath, float sR) { + try { + Mat5File mfr = Mat5.readFromFile(filePath); + + // //get array of a name "my_array" from file + Struct mlArrayRetrived = mfr.getStruct( "clickpreds" ); + + int numClicks= mlArrayRetrived.getNumCols(); + ArrayList clicks = new ArrayList(numClicks); + + GroupedRawData clickData; + for (int i=0; i { //run a model if it is waveform info. float[][] waveStack = new float[transformedDataStack.length][]; for (int i=0; i { ((GenericModelParams) genericParams).defaultShape = longArr2Long(genericModel.getInputShape().getShape()); ((GenericModelParams) genericParams).defualtOuput = longArr2Long(genericModel.getOutShape().getShape()); - + } catch (Exception e) { diff --git a/src/rawDeepLearningClassifier/layoutFX/DLSettingsPane.java b/src/rawDeepLearningClassifier/layoutFX/DLSettingsPane.java index b2c8c0e4..f08a9b02 100644 --- a/src/rawDeepLearningClassifier/layoutFX/DLSettingsPane.java +++ b/src/rawDeepLearningClassifier/layoutFX/DLSettingsPane.java @@ -3,12 +3,14 @@ package rawDeepLearningClassifier.layoutFX; import java.util.ArrayList; import org.controlsfx.control.PopOver; +import org.controlsfx.control.ToggleSwitch; import PamController.PamGUIManager; import PamController.SettingsPane; import PamDetection.RawDataUnit; import PamView.dialog.warn.WarnOnce; import PamguardMVC.PamDataBlock; +import PamguardMVC.PamRawDataBlock; import PamguardMVC.dataSelector.NullDataSelectorCreator; import clickDetector.ClickDetection; import clipgenerator.ClipDataUnit; @@ -116,6 +118,10 @@ public class DLSettingsPane extends SettingsPane{ private DLModelSelectPane modelSelectPane; + private PamToggleSwitch segEnableSwitch; + + private PamGridPane segmenterGridPane; + public DLSettingsPane(DLControl dlControl){ @@ -178,11 +184,26 @@ public class DLSettingsPane extends SettingsPane{ vBox.getChildren().add(createDataSelectorPane()); - // the segmentation params - Label label = new Label("Segmentation"); - PamGuiManagerFX.titleFont2style(label); + // the segmentation parameters + Label segLabel = new Label("Segmentation"); + PamGuiManagerFX.titleFont2style(segLabel); - vBox.getChildren().add(label); + + segEnableSwitch = new PamToggleSwitch("Enable"); + segEnableSwitch.selectedProperty().addListener((obsVal, oldVal, newVal)->{ + enableControls(); + }); + segEnableSwitch.setVisible(false); //set visible by default + segEnableSwitch.setAlignment(Pos.CENTER_RIGHT); + + + //segmentation can have an option to disable for certain input datablocks + PamBorderPane segmenterPane = new PamBorderPane(); + segmenterPane.setLeft(segLabel); + segmenterPane.setRight(segEnableSwitch); + + //add to the main pane + vBox.getChildren().add(segmenterPane); windowLength = new PamSpinner(0, Integer.MAX_VALUE, 10, 10000); windowLength.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); @@ -211,7 +232,7 @@ public class DLSettingsPane extends SettingsPane{ hopLength.getValueFactory().setValue(Math.round(windowLength.getValue()/2)); }); - PamGridPane segmenterGridPane = new PamGridPane(); + segmenterGridPane = new PamGridPane(); segmenterGridPane.setHgap(5); ColumnConstraints col1 = new ColumnConstraints(); @@ -244,35 +265,16 @@ public class DLSettingsPane extends SettingsPane{ vBox.getChildren().add(label2); - /** - * Pane which allows users to select a model type. - */ + //Pane which allows users to select a model from a file, link or default model. modelSelectPane = new DLModelSelectPane(this); - // //add the possible deep learning models. - // dlModelBox= new ComboBox(); - // for (int i=0; i{ - // setClassifierPane(); - // if (mainPane!=null) { - // if (mainPane.getScene().getWindow() instanceof Stage) { - // Stage stage = (Stage) mainPane.getScene().getWindow(); - // stage.sizeToScene(); - // } - // } - // //this.dlControl.getAnnotationType().getSymbolModifier(symbolChooser). - // }); - // - // vBox.getChildren().add(dlModelBox); - + //create pane which shows the classifier settings classifierPane = new PamBorderPane(); - vBox.getChildren().addAll(modelSelectPane, classifierPane); + //bump this in case no settings + segEnableSwitch.setSelected(true); + vBox.getChildren().addAll(modelSelectPane, classifierPane); return vBox; } @@ -354,6 +356,20 @@ public class DLSettingsPane extends SettingsPane{ } dataSelectorButton.setDisable(!dataSelectorCheckBox.isSelected()); + + boolean segEnable = true; //should we enable segmenter controls + if (sourcePane.getSource() instanceof PamRawDataBlock) { + //if a raw data block then we always enable segmentation no matter what + segEnable=true; + segEnableSwitch.setVisible(false); + } + else { + segEnable = segEnableSwitch.isSelected(); + segEnableSwitch.setVisible(true); + } + + segmenterGridPane.setDisable(!segEnable); + infoLabel.setDisable(!segEnable); } @@ -380,11 +396,11 @@ public class DLSettingsPane extends SettingsPane{ * Set the classifier pane. */ protected void setClassifierPane() { - - + + //set the classifier Pane.class if (modelSelectPane.currentClassifierModel!=null && modelSelectPane.currentClassifierModel.getModelUI()!=null) { - + classifierPane.setCenter(modelSelectPane.currentClassifierModel.getModelUI().getSettingsPane().getContentNode()); if (modelSelectPane.currentClassifierModel!=null) { @@ -456,7 +472,7 @@ public class DLSettingsPane extends SettingsPane{ // } currParams.useDataSelector = dataSelectorCheckBox.isSelected(); - + if (dlControl.getDataSelector()!=null) { dlControl.getDataSelector().getDialogPaneFX().getParams(true); } @@ -471,6 +487,8 @@ public class DLSettingsPane extends SettingsPane{ currParams.modelURI = this.modelSelectPane.currentSelectedFile; + currParams.enableSegmentation = segEnableSwitch.isSelected(); + return currParams; } @@ -555,10 +573,12 @@ public class DLSettingsPane extends SettingsPane{ setClassifierPane(); - enableControls(); - setSegInfoLabel(); + segEnableSwitch.setSelected(currParams.enableSegmentation); + + enableControls(); + // //set up the model and the custom pane if necessary. this.modelSelectPane.loadNewModel(currParams.modelURI); //this.modelSelectPane.updatePathLabel(); diff --git a/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/DataTransformPaneFactory.java b/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/DataTransformPaneFactory.java index bfc9f559..78b3a937 100644 --- a/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/DataTransformPaneFactory.java +++ b/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/DataTransformPaneFactory.java @@ -66,6 +66,10 @@ public class DataTransformPaneFactory { settingsPane = new FilterTransformPane(dlTransfrom); settingsPane.setParams(dlTransfrom); break; + case PEAK_TRIM: + settingsPane = new PeakTrimTransformPane(dlTransfrom); + settingsPane.setParams(dlTransfrom); + break; case SPEC2DB: // settingsPane = new LabelTransfromPane(dlTransfrom, DLTransformType.SPEC2DB.toString()); // settingsPane.setPadding(new Insets(0,0,0,20)); diff --git a/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/PeakTrimTransformPane.java b/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/PeakTrimTransformPane.java new file mode 100644 index 00000000..5a7bdcef --- /dev/null +++ b/src/rawDeepLearningClassifier/layoutFX/dlTransfroms/PeakTrimTransformPane.java @@ -0,0 +1,115 @@ +package rawDeepLearningClassifier.layoutFX.dlTransfroms; + +import org.jamdev.jdl4pam.transforms.DLTransform; +import org.jamdev.jdl4pam.transforms.SimpleTransform; +import org.jamdev.jpamutils.wavFiles.AudioData; +import javafx.geometry.Pos; +import javafx.scene.Node; +import javafx.scene.control.ComboBox; +import javafx.scene.control.Label; +import javafx.scene.control.Spinner; +import javafx.scene.control.TitledPane; +import pamViewFX.fxNodes.PamHBox; +import pamViewFX.fxNodes.utilityPanes.SimpleFilterPaneFX; + +/** + * Pane for a peak search trim transform. + * + * @author Jamie Macaulay + * + */ +public class PeakTrimTransformPane extends DLTransformPane { + + /** + * The transform associated with the settings pane. + */ + private DLTransform simpleTransfrom; + + /** + * Controls for changing peak search settings. + */ + private SimpleFilterPaneFX filterPane; + + /** + * Choice box for changing the type of peak search algorithm + */ + private ComboBox peakSelectionBox; + + /** + * Spinner for changing the target length + */ + private Spinner targetLenSpinner; + + + public PeakTrimTransformPane(DLTransform dlTransfrom) { + super(); + this.simpleTransfrom= dlTransfrom; + this.setCenter(createFilterPane()); +// this.setStyle("-fx-background-color:orangered;"); + + } + + private Node createFilterPane() { + + peakSelectionBox = new ComboBox(); + peakSelectionBox.getItems().add(AudioData.PEAK_MAX, "Max. Peak"); + + peakSelectionBox.valueProperty().addListener((obsVal, oldVal, newVal)->{ + this.notifySettingsListeners(); + }); + + //spinner for changing filter order. + targetLenSpinner = new Spinner(1,50,4,1); + targetLenSpinner.valueProperty().addListener((obsVal, oldVal, newVal)->{ + this.notifySettingsListeners(); + }); + targetLenSpinner.getStyleClass().add(Spinner.STYLE_CLASS_SPLIT_ARROWS_HORIZONTAL); + + PamHBox filterTypeHolder = new PamHBox(); + filterTypeHolder.setSpacing(5); + filterTypeHolder.setAlignment(Pos.CENTER_LEFT); + filterTypeHolder.getChildren().addAll(peakSelectionBox, new Label("Target length"), targetLenSpinner); + + TitledPane titledPane = new TitledPane(simpleTransfrom.getDLTransformType().toString(), filterTypeHolder); + + // PamBorderPane borderPane = new PamBorderPane(); + // borderPane.setTop(new Label(simpleTransfrom.getDLTransformType().toString())); + // borderPane.setCenter(hBox); + + titledPane.setExpanded(false); + + return titledPane; + } + + + @Override + public DLTransform getDLTransform() { + return this.getParams(simpleTransfrom) ; + } + + @Override + public DLTransform getParams(DLTransform dlTransform) { + +// System.out.println("GET PARAMS: FILTER"); + + SimpleTransform simpleTransform = (SimpleTransform) dlTransform; + + simpleTransform.setParams(new Number[]{targetLenSpinner.getValue(), peakSelectionBox.getSelectionModel().getSelectedIndex()}); + + return simpleTransform; + } + + @Override + public void setParams(DLTransform dlTransform) { + +// System.out.println("SET PARAMS: FILTER"); + + SimpleTransform simpleTransform = (SimpleTransform) dlTransform; + + //get the selection model. + peakSelectionBox.getSelectionModel().select(simpleTransform.getParams()[1].intValue()); + targetLenSpinner.getValueFactory().setValue(simpleTransform.getParams()[0].intValue()); + + } + +} diff --git a/src/rawDeepLearningClassifier/segmenter/GroupedRawData.java b/src/rawDeepLearningClassifier/segmenter/GroupedRawData.java index e969e552..e32200a0 100644 --- a/src/rawDeepLearningClassifier/segmenter/GroupedRawData.java +++ b/src/rawDeepLearningClassifier/segmenter/GroupedRawData.java @@ -114,6 +114,15 @@ public class GroupedRawData extends PamDataUnit implements PamDetection, Cloneab public double[][] getRawData() { return rawData; } + + + /** + * Set the raw data grouped by channel. + * @param the raw acoustic data to set + */ + public void setRawData(double[][] rawData) { + this.rawData=rawData; + } /** * Get the current pointer for rawData. diff --git a/src/rawDeepLearningClassifier/segmenter/SegmenterProcess.java b/src/rawDeepLearningClassifier/segmenter/SegmenterProcess.java index 7012ac68..541f1048 100644 --- a/src/rawDeepLearningClassifier/segmenter/SegmenterProcess.java +++ b/src/rawDeepLearningClassifier/segmenter/SegmenterProcess.java @@ -6,7 +6,6 @@ import java.util.Arrays; import PamController.PamController; import PamDetection.RawDataUnit; -import PamUtils.PamArrayUtils; import PamUtils.PamUtils; import PamView.GroupedSourceParameters; import PamView.PamDetectionOverlayGraphics; @@ -486,9 +485,20 @@ public class SegmenterProcess extends PamProcess { //pass the raw click data to the segmenter for (int i=0;i dlTransformParamsArr = new ArrayList(); + + //waveform transforms. + dlTransformParamsArr.add(new SimpleTransformParams(DLTransformType.DECIMATE_SCIPY, 248000.)); + dlTransformParamsArr.add(new SimpleTransformParams(DLTransformType.PEAK_TRIM, 128, 1)); + dlTransformParamsArr.add(new SimpleTransformParams(DLTransformType.NORMALISE_WAV, 0., 1, AudioData.ZSCORE)); + + genericModelParams.dlTransfromParams = dlTransformParamsArr; + genericModelParams.dlTransfroms = DLTransformsFactory.makeDLTransforms((ArrayList)genericModelParams.dlTransfromParams); + + //create the clicks. + path = Paths.get(clicksPath); + ArrayList clicks = importClicks(path.toAbsolutePath().normalize().toString(), SAMPLE_RATE); + + //prep the model + genericModelWorker.prepModel(genericModelParams, null); + + System.out.println("Model has loaded"); + + ArrayList groupedData = new ArrayList(); + + for (int i=0; i<1; i++) { + + float prediction = (float) clicks.get(i).getPrediction()[0]; + + groupedData.add(clicks.get(i)); //TODO for loop + + //System.out.println("Waveform input: " + groupedData.get(i).getRawData().length + " " + groupedData.get(i).getRawData()[0].length); + + ArrayList genericPrediction = genericModelWorker.runModel(groupedData,SAMPLE_RATE, 0); + + float[] output = genericPrediction.get(i).getPrediction(); + + System.out.println(String.format("Click %d Predicted output: %.2f true output: %.2f passed: %b", clicks.get(i).getUID(), + output[0], prediction, output[0]>prediction*0.9 && output[0] importClicks(String filePath, float sR) { + + try { + Mat5File mfr = Mat5.readFromFile(filePath); + + // //get array of a name "my_array" from file + Struct mlArrayRetrived = mfr.getStruct( "clickpreds" ); + + int numClicks= mlArrayRetrived.getNumCols(); + ArrayList clicks = new ArrayList(numClicks); + + PredGroupedRawData clickData; + for (int i=0; i