Merge from DG with fixes to MatchedTemplate classifier. (#119)

* Stop command

small change so command is available as a constant

* Lots of small updates to enable opening of a secondary configuration for
batch processing control.

* Fix user input bug in viewer

which created exponential copies of user comments!

* Adding TAST trigger alarm action

To be competed when GW provide correct string for interface

* Echo offline detection

Fix up affected datablocks for offline echo detection

* fix module import

System for importing modules from other psfx files was not working.
Probably wasn't working for quite some time. Now fixed.

* Bug fixes to Match Template classifier

1) When a large template was imported only 1: fftLength of the mathc waveform was used and so clicks would be correlated with noise. The peak of the template is now used when the peak search function is selected.
2) The plus button in the tab pane had disappeared.
3) Seems like the decimators were the wrong round. So the waveforms were using an up sample function when they should have been using a decimator function. and vice versa...major bug when using different sample rates!

* Fix merge

Merged in a single commit from Jamies fork that had updates to the
template classifier. Then had to make a few changes to get it working
with other changes J had made that must have been in other commits.

---------

Co-authored-by: Jamie Mac <macster110@gmail.com>
This commit is contained in:
Douglas Gillespie 2023-12-18 15:19:22 +00:00 committed by GitHub
parent 667b68e231
commit 291b00e1b1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
17 changed files with 971 additions and 281 deletions

View File

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

View File

@ -22,19 +22,19 @@ public class PamArrayUtils {
/**
* Calculate the mean of one dimension within a list of points. <i>e.g.</i> the points might be a list of [x y z] co-ordinates in
* which case the dim=0 would return the mean of all x points.
* @param array - a list of points
* @param successJump - a list of points
* @param InitialtoIgnorePercentage: ignore the first percentage of results
* @param dim - the dimension of the point to calculate the average for
* @return the mean of one dimension of the list of the points.
*/
public static double mean(ArrayList<double[]> array, double InitialtoIgnorePercentage, int dim){
public static double mean(ArrayList<double[]> successJump, double InitialtoIgnorePercentage, int dim){
double meanTotal=0;
int n=0;
int forStart=(int) Math.round((InitialtoIgnorePercentage)*array.size());
int forStart=(int) Math.round((InitialtoIgnorePercentage)*successJump.size());
for (int i=forStart; i<array.size();i++){
meanTotal+= array.get(i)[dim];
for (int i=forStart; i<successJump.size();i++){
meanTotal+= successJump.get(i)[dim];
n++;
}
@ -42,24 +42,25 @@ public class PamArrayUtils {
double mean=meanTotal/n;
return mean;
}
/**
* Calculate the standard deviation of an array of doubles, ignoring an 'initialtoIgnorePercentage' percentage of jumps
* @param array
* @param successJump
* @param initialtoIgnorePercentage- percentage of initial values to ignore.
* @return standard deviation of array.
*/
public static double std(ArrayList<double[]> array, double initialtoIgnorePercentage, int dim){
public static double std(ArrayList<double[]> successJump, double initialtoIgnorePercentage, int dim){
double std=0.0;
int n=0;
int forStart=(int) Math.round((initialtoIgnorePercentage)*array.size());
int forStart=(int) Math.round((initialtoIgnorePercentage)*successJump.size());
double meanTotal= mean(array, initialtoIgnorePercentage, dim);
double meanTotal= mean(successJump, initialtoIgnorePercentage, dim);
//calculate standard deviation
for (int k=forStart;k<array.size(); k++){
std+=Math.pow((array.get(k)[dim]-meanTotal),2);
for (int k=forStart;k<successJump.size(); k++){
std+=Math.pow((successJump.get(k)[dim]-meanTotal),2);
}
//standard deviation
@ -448,19 +449,6 @@ public class PamArrayUtils {
return new int[] {min, max};
}
/**
* Calculate the maximum value in an array
* @param arr - the array to find the maximum value of.
* @return the maximum value in the array
*/
public static double max(double[] arr) {
double max = Double.NEGATIVE_INFINITY;
for(double cur: arr)
max = Math.max(max, cur);
return max;
}
/**
* Calculate the maximum value in an array
@ -475,6 +463,20 @@ public class PamArrayUtils {
return max;
}
/**
* Calculate the maximum value in an array
* @param arr - the array to find the maximum value of.
* @return the maximum value in the array
*/
public static double max(double[] arr) {
double max = Double.NEGATIVE_INFINITY;
for(double cur: arr)
max = Math.max(max, cur);
return max;
}
/**
* Calculate the maximum value in an array
@ -489,6 +491,50 @@ public class PamArrayUtils {
return max;
}
/**
* Get the index of the maximum value in an array
* @param arr - the array to find the position of the maximum value.
* m value of.
* @return the index of the maximum value
*/
public static int maxPos(double[] arr) {
double max = Double.NEGATIVE_INFINITY;
int index = -1;
int count = 0;
for(double cur: arr) {
if (cur>max) {
index = count;
max=cur;
}
count++;
}
return index;
}
/**
* Get the minimum index of an array
* @param arr - the array to find the position of the maximum value.
* m value of.
* @return the index of the minimum value
*/
public static int minPos(double[] arr) {
double max = Double.POSITIVE_INFINITY;
int index = -1;
int count = 0;
for(double cur: arr) {
if (cur<max) {
index = count;
max=cur;
}
count++;
}
return index;
}
/**
* Get the minimum value in an array
@ -504,6 +550,8 @@ public class PamArrayUtils {
return min;
}
/**
* Get the minimum value in an array
@ -578,6 +626,27 @@ public class PamArrayUtils {
ArrayUtils.reverse(clone);
return clone;
}
/**
* Split an array based on start index and end index. A new array with index 0
* as start and the last index at end is created and data from the input array
* copied to it.
*
* @param arr - the array to split
* @param start - the start index
* @param end - the end index
* @return the split array.
*/
public static double[][] split(double[][] arr, int start, int end) {
double[][] newArr = new double[end - start][];
int n = 0;
for (int i = start; i < end; i++) {
newArr[n] = arr[i];
n++;
}
return newArr;
}
/**
* Sum the elements in an array
@ -855,5 +924,152 @@ public class PamArrayUtils {
return arr;
}
}
/**
* Convert a 2D float array to a 2D double array.
* @param arrf - the float array
* @return a double array containing the same numbers as arrf.
*/
public static double[][] float2Double(float[][] arrf) {
double[][] newArray = new double[arrf.length][];
for (int i=0; i<arrf.length; i++) {
newArray[i] = float2Double(arrf[i]);
}
return newArray;
}
/**
* Convert a float array to a double array.
* @param arrd - the double array
* @return a double array containing the same numbers as arrf.
*/
public static float[] double2Float(double[] arrd) {
float[] arr = new float[arrd.length];
for (int i=0; i<arr.length; i++) {
arr[i] = (float) arrd[i];
}
return arr;
}
/**
* Convert a 2D float array to a 2D double array.
* @param arrd - the double array
* @return a double array containing the same numbers as arrf.
*/
public static float[][] double2Float(double[][] arrd) {
float[][] newArray = new float[arrd.length][];
for (int i=0; i<arrd.length; i++) {
newArray[i] = double2Float(arrd[i]);
}
return newArray;
}
/**
* Check if two int arrays contain the same elements
* @param arr1 - the array to compare to.
* @param arr2 - the array to be compared.
*/
public static boolean arrEquals(int[] arr1, int[] arr2) {
if (arr1.length!=arr2.length) return false;
for (int i =0 ;i<arr1.length; i++) {
if (arr1[i]!=arr2[i]) return false;
}
return true;
}
/**
* Check if two double arrays contain the same elements
* @param arr1 - the array to compare to.
* @param arr2 - the array to be compared.
*/
public static boolean arrEquals(double[] arr1, double[] arr2) {
if (arr1.length!=arr2.length) return false;
for (int i =0 ;i<arr1.length; i++) {
if (arr1[i]!=arr2[i]) return false;
}
return true;
}
/**
* Convert primitive long array to Long object array.
* @param arr - primitive long array
* @return a Long array
*/
public static Long[] primitive2Object(long[] arr) {
if (arr==null) return null;
Long[] arrL = new Long[arr.length];
for (int i=0; i<arr.length; i++) {
arrL[i]=arr[i];
}
return arrL;
}
/**
* Convert primitive double array to Double object array.
* @param arr - primitive double array
* @return a Double array
*/
public static Double[] primitive2Object(double[] arr) {
if (arr==null) return null;
Double[] arrL = new Double[arr.length];
for (int i=0; i<arr.length; i++) {
arrL[i]=arr[i];
}
return arrL;
}
/**
* Convert primitive int array to Integer object array.
* @param arr - primitive int array
* @return a Ineteger array
*/
public static Integer[] primitive2Object(int[] arr) {
if (arr==null) return null;
Integer[] arrL = new Integer[arr.length];
for (int i=0; i<arr.length; i++) {
arrL[i]=arr[i];
}
return arrL;
}
public static long[] object2Primitve(Long[] arr) {
if (arr==null) return null;
long[] arrL = new long[arr.length];
for (int i=0; i<arr.length; i++) {
arrL[i]=arr[i].longValue();
}
return arrL;
}
}

View File

@ -6,6 +6,7 @@ import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.math.BigDecimal;
import java.text.DecimalFormat;
import java.text.NumberFormat;
import java.util.ArrayList;
@ -89,10 +90,20 @@ public class TxtFileUtils {
//5/08/2022 - there was a bug here where there is some sort of invisible character that does not appear on the
//print screen - the only way you can tell is the char array is greater than the number of digits - removed all non numeric
//characters.
// updated again on 15/11/23 to include - signs, or you end up with the abs(of every number!)
String number = new String(recordsOnLine[i].strip().replaceAll("[^\\d.-]", ""));
dat = Double.valueOf(number);
//dat = DecimalFormat.getNumberInstance().parse(new String(recordsOnLine[i].strip().toCharArray())).doubleValue();
// if (recordsOnLine[i].contains("e") || recordsOnLine[i].contains("E")) {
// //need to check whether scientific notation might be used here
//// dat = new BigDecimal(recordsOnLine[i]).doubleValue();
// dat = Double.valueOf(recordsOnLine[i]);
// }
// else {
//note that this gets rid of numbers like 3.84e+05
// updated again on 15/11/23 to include - signs, or you end up with the abs(of every number!)
//also added E and e to neglected characters to ensure scientific notation can be used,
String number = new String(recordsOnLine[i].strip().replaceAll("[^Ee\\d.-]", ""));
dat = Double.valueOf(number);
//dat = DecimalFormat.getNumberInstance().parse(new String(recordsOnLine[i].strip().toCharArray())).doubleValue();
// }
}
catch (Exception e){
e.printStackTrace();

View File

@ -395,7 +395,7 @@ public class ClickBTDisplay extends ClickDisplay implements PamObserver, PamSett
int subDetInd = superDet.findSubdetectionInfo(clickDetection);
for (int i = subDetInd-1; i >= 0; i--) {
PamDataUnit subDet = superDet.getSubDetection(i);
if (subDet.getChannelBitmap() == clickDetection.getChannelBitmap()) {
if (subDet!=null && subDet.getChannelBitmap() == clickDetection.getChannelBitmap()) {
double ici = (double) (clickDetection.getTimeMilliseconds() - subDet.getTimeMilliseconds())/1000.;
clickDetection.setTempICI(ici);
break;

View File

@ -1,13 +1,18 @@
package clickDetector.layoutFX.clickClassifiers;
import pamViewFX.fxNodes.PamBorderPane;
import pamViewFX.fxNodes.PamSymbolFX;
import pamViewFX.fxNodes.flipPane.PamFlipPane;
import pamViewFX.fxNodes.table.TableSettingsPane;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.canvas.Canvas;
import javafx.scene.control.Button;
import javafx.scene.control.Dialog;
import javafx.scene.control.TableCell;
import javafx.scene.control.TableColumn;
import javafx.scene.control.cell.CheckBoxTableCell;
import clickDetector.BasicClickIdParameters;
@ -15,6 +20,7 @@ import clickDetector.ClickControl;
import clickDetector.ClickTypeParams;
import clickDetector.ClickClassifiers.ClickIdentifier;
import clickDetector.ClickClassifiers.basic.BasicClickIdentifier;
import clickDetector.ClickClassifiers.basicSweep.*;
/**
* Pane for the basic click classifier.
@ -23,33 +29,33 @@ import clickDetector.ClickClassifiers.basic.BasicClickIdentifier;
*
*/
public class BasicIdentifierPaneFX implements ClassifyPaneFX {
/**
* Reference to the basicClickIdentifier.
*/
private ClickIdentifier basicClickIdentifier;
/**
* Reference to the click control.
*/
protected ClickControl clickControl;
/**
* Pane which holds table data.
*/
private TableSettingsPane<ClickTypeProperty> settingsPane;
// /**
// * Hiding pane which slides out to allow users to change click type settings.
// */
// protected HidingPane hidingPane;
protected TableSettingsPane<ClickTypeProperty> clickTypesTable;
// /**
// * Hiding pane which slides out to allow users to change click type settings.
// */
// protected HidingPane hidingPane;
/**
* Holds click classifier controls inside hiding pane.
*/
protected PamBorderPane clickTypeHolder;
/**
* A list of click classifiers currently shown in the table.It Would have been much easier to have this in params
* but didn't want to add any FX related as these should be GUI independent classes.
@ -60,19 +66,19 @@ public class BasicIdentifierPaneFX implements ClassifyPaneFX {
* Holds the table.
*/
protected PamBorderPane mainHolderPane;
// /**
// * The width of the hiding pane
// */
// private double hidingPaneWidth=900;
// /**
// * The width of the hiding pane
// */
// private double hidingPaneWidth=900;
/**
* Cloned copy of BasicClickIdParameters.
*/
private BasicClickIdParameters basicClickIdParameters;
private PamBorderPane mainPane;
private PamFlipPane flipPane;
/**
@ -85,7 +91,7 @@ public class BasicIdentifierPaneFX implements ClassifyPaneFX {
this.basicClickIdentifier= basicClickIdentifier;
this.clickControl=clickControl;
mainPane= new PamBorderPane();
flipPane = new PamFlipPane();
flipPane.getFrontPane().setCenter(createSettingsPane());
flipPane.getBackPane().setCenter(clickTypeHolder);
@ -93,35 +99,36 @@ public class BasicIdentifierPaneFX implements ClassifyPaneFX {
mainPane.setCenter(flipPane);
}
/**
* Create the controls for the basic click identifier pane.
* @return node with all controls for basic click classifier.
*/
protected Node createSettingsPane(){
mainHolderPane=new PamBorderPane();
mainHolderPane.setCenter(settingsPane=new ClickClassifierTable(clickClassifiers));
mainHolderPane.setPadding(new Insets(5,5,5,5));
mainHolderPane.setCenter(clickTypesTable=new ClickClassifierTable(clickClassifiers));
clickTypeHolder=new PamBorderPane();
//clickTypeHolder.setPrefWidth(hidingPaneWidth);
return mainHolderPane;
}
// /**
// * Added purely so can be override and hiding pane set in different location if required
// */
// public void createHidingPane(){
// hidingPane=new HidingPane(Side.RIGHT, clickTypeHolder, mainPane, false);
// //hidingPane.showHidePane(false);
// mainHolderPane.setRight(hidingPane);
// hidingPane.showHidePane(false);
// }
// /**
// * Added purely so can be override and hiding pane set in different location if required
// */
// public void createHidingPane(){
// hidingPane=new HidingPane(Side.RIGHT, clickTypeHolder, mainPane, false);
// //hidingPane.showHidePane(false);
// mainHolderPane.setRight(hidingPane);
// hidingPane.showHidePane(false);
// }
@Override
@ -133,7 +140,7 @@ public class BasicIdentifierPaneFX implements ClassifyPaneFX {
public void setParams() {
basicClickIdParameters = ((BasicClickIdentifier) basicClickIdentifier).getIdParameters().clone();
//change the observable list.
}
@Override
@ -151,9 +158,9 @@ public class BasicIdentifierPaneFX implements ClassifyPaneFX {
@Override
public void setActive(boolean b) {
// TODO Auto-generated method stub
}
/**
* Class which extends TableSettingsPane and creates a sliding pane instead of a dialog when an item is added.
* @author Jamie Macaulay
@ -164,33 +171,43 @@ public class BasicIdentifierPaneFX implements ClassifyPaneFX {
public ClickClassifierTable(ObservableList<ClickTypeProperty> data) {
super(data);
//need to set up all the rows.
TableColumn<ClickTypeProperty,String> name = new TableColumn<ClickTypeProperty,String>("Name");
name.setCellValueFactory(cellData -> cellData.getValue().name);
name.setEditable(true);
TableColumn<ClickTypeProperty,String> icon = new TableColumn<ClickTypeProperty,String>("Name");
icon.setCellValueFactory(cellData -> cellData.getValue().name);
icon.setEditable(false);
icon.setCellFactory((tableColumn) -> {
TableCell<ClickTypeProperty, String> tableCell = new ClickTypeNameCell();
return tableCell;
});
// TableColumn<ClickTypeProperty,String> name = new TableColumn<ClickTypeProperty,String>("Name");
// name.setCellValueFactory(cellData -> cellData.getValue().name);
// name.setEditable(true);
TableColumn<ClickTypeProperty,Number> code = new TableColumn<ClickTypeProperty,Number>("Species Code");
code.setCellValueFactory(cellData -> cellData.getValue().code);
TableColumn<ClickTypeProperty,Boolean> checkCol = new TableColumn<>("Enable");
checkCol.setCellValueFactory( cellData -> cellData.getValue().enableClassifier);
checkCol.setCellFactory(CheckBoxTableCell.forTableColumn(checkCol));
checkCol.setEditable(true);
checkCol.setMaxWidth( 100 );
checkCol.setMinWidth( 100 );
TableColumn<ClickTypeProperty,Boolean> discard = new TableColumn<>("Discard");
discard.setCellValueFactory( cellData -> cellData.getValue().discardClassifier);
discard.setCellFactory(CheckBoxTableCell.forTableColumn(checkCol));
discard.setEditable(true);
discard.setMaxWidth( 100 );
discard.setMinWidth( 100 );
getTableView().setEditable(true);
getTableView().getColumns().addAll(checkCol, name, code, discard);
getTableView().getColumns().addAll(checkCol, icon, code, discard);
}
@ -206,33 +223,33 @@ public class BasicIdentifierPaneFX implements ClassifyPaneFX {
showFlipPane(true);
return null;
}
@Override
public void editData(ClickTypeProperty data){
setClassifierPane(data);
showFlipPane(true);
}
@Override
public void createNewData(){
//create a new classifier.
clickClassifiers.add(createClickTypeProperty());
}
}
// /**
// * Show the hiding pane which contains classifier settings
// * NOTE: needed to add this to stop a stack overflow error in BasicClickIdentifier 06/09/2016
// * @param show - true to show pane.
// */
// public void showHidingPane(boolean show){
// if (hidingPane==null){
// this.createHidingPane();
// }
// hidingPane.showHidePane(true);
// }
// /**
// * Show the hiding pane which contains classifier settings
// * NOTE: needed to add this to stop a stack overflow error in BasicClickIdentifier 06/09/2016
// * @param show - true to show pane.
// */
// public void showHidingPane(boolean show){
// if (hidingPane==null){
// this.createHidingPane();
// }
// hidingPane.showHidePane(true);
// }
/**
* Show the flip pane.
* NOTE: needed to add this to stop a stack overflow error in BasicClickIdentifier 06/09/2016
@ -247,33 +264,34 @@ public class BasicIdentifierPaneFX implements ClassifyPaneFX {
flipPane.flipToFront();
}
}
/**
* Create click classifier.
*/
public ClickTypeProperty createClickTypeProperty(){
return new ClickTypeProperty(new ClickTypeParams(clickClassifiers.size()));
}
/**
* Set classifier pane within hiding pane.
* @param clickTypeProperty
*/
public void setClassifierPane(ClickTypeProperty clickTypeProperty){
ClickTypePaneFX clickTypePane=new ClickTypePaneFX();
clickTypePane.setParams(clickTypeProperty);
clickTypeHolder.setCenter(clickTypePane.getContentNode());
//now need to make sure on closing the pane that settings are saved. Need to
//remove the old click type from the list and add new one in the same position.
getFlipPaneCloseButton().setOnAction((action)->{
//System.out.println("CLOSE FLIP PANE");
showFlipPane(false);
//this should update the click type property in the observable list thus changing the table
clickTypePane.getParams(clickTypeProperty);
//need to refresh table to show symbol.
clickTypesTable.getTableView().refresh();
});
}
@ -285,16 +303,16 @@ public class BasicIdentifierPaneFX implements ClassifyPaneFX {
return flipPane.getBackButton();
}
// /**
// * Get the hiding pane which holds settings for different click types.
// * @return the HidingPane for click classifiers.
// */
// public HidingPane getClickTypeHidingPane() {
// if (hidingPane==null) {
// this.createHidingPane();
// }
// return hidingPane;
// }
// /**
// * Get the hiding pane which holds settings for different click types.
// * @return the HidingPane for click classifiers.
// */
// public HidingPane getClickTypeHidingPane() {
// if (hidingPane==null) {
// this.createHidingPane();
// }
// return hidingPane;
// }
/**
* Get the pane which holds the ClickTypePaneFX.
@ -303,15 +321,15 @@ public class BasicIdentifierPaneFX implements ClassifyPaneFX {
public PamBorderPane getClickTypeHolder() {
return clickTypeHolder;
};
/**
* Get the table which holds a list of classifier
* @return table which holds a list of classifiers
*/
public TableSettingsPane<ClickTypeProperty> getTablePane() {
return this.settingsPane;
return this.clickTypesTable;
};
/**
* Get list of click classifiers
* @return list of click classifiers
@ -329,4 +347,48 @@ public class BasicIdentifierPaneFX implements ClassifyPaneFX {
return flipPane;
}
/**
* Shows a name alongside a graphic showing the current symbol.
* @author Jamie Macaulay.
*
*/
private class ClickTypeNameCell extends TableCell<ClickTypeProperty, String> {
/**
* Symbol is drawn on the canvas
*/
Canvas canvas;
public static final double SYMBOL_SIZE = 20;
public ClickTypeNameCell() {
super();
canvas = new Canvas(SYMBOL_SIZE, SYMBOL_SIZE);
}
@Override
protected void updateItem(String item, boolean empty) {
super.updateItem(item, empty);
this.setText(item);
this.setGraphic(null);
if (this.getTableRow()!=null && this.getTableRow().getItem()!=null) {
SweepClassifierSet clickProperty = (SweepClassifierSet) this.getTableRow().getItem().getClickType();
if (clickProperty.symbol!=null) {
canvas.getGraphicsContext2D().clearRect(0, 0, SYMBOL_SIZE, SYMBOL_SIZE);
PamSymbolFX pamSymbolFX = new PamSymbolFX(clickProperty.symbol);
pamSymbolFX.draw(canvas.getGraphicsContext2D(), new Point2D(SYMBOL_SIZE/2,SYMBOL_SIZE/2), SYMBOL_SIZE, SYMBOL_SIZE);
this.setGraphic(canvas);
}
}
}
};
}

View File

@ -1,10 +1,16 @@
package clickDetector.layoutFX.clickClassifiers;
import clickDetector.ClickControl;
import clickDetector.ClickClassifiers.basicSweep.SweepClassifier;
import clickDetector.ClickClassifiers.basicSweep.SweepClassifierParameters;
import clickDetector.ClickClassifiers.basicSweep.SweepClassifierSet;
import javafx.scene.text.FontPosture;
import javafx.scene.text.FontWeight;
import javafx.scene.layout.Region;
import javafx.scene.text.Font;
import javafx.geometry.Insets;
/**
* Slightly different pane for the sweep classifier.
@ -38,12 +44,17 @@ public class SweepClassifierPaneFX extends BasicIdentifierPaneFX {
@Override
public void setClassifierPane(ClickTypeProperty clickTypeProperty){
SweepClassifierSetPaneFX sweepPane=new SweepClassifierSetPaneFX(sweepClickClassifier);
//set padding - want the flip pane not to have padding so back button reaches edge of node.
((Region) sweepPane.getContentNode()).setPadding(new Insets(5,5,5,5));
//make it so the title of the pane is the same as the name as the classifier
getFlipPane().getAdvLabel().textProperty().unbind();
getFlipPane().getAdvLabel().textProperty().bind( sweepPane.getNameTextProperty());
getFlipPane().getAdvLabel().textProperty().bind(sweepPane.getNameTextProperty());
// removed DG 2023 12 14 since not everything was in this merge that was required Needs to be put back.
// getFlipPane().getPreAdvLabel().graphicProperty().bind(sweepPane.getNameGraphicProperty());
sweepPane.classifierItemRow = sweepClickClassifier.getSweepClassifierParams().getSetRow((SweepClassifierSet) clickTypeProperty.getClickType());
sweepPane.setParams(clickTypeProperty);
@ -54,6 +65,8 @@ public class SweepClassifierPaneFX extends BasicIdentifierPaneFX {
getFlipPaneCloseButton().setOnAction((action)->{
showFlipPane(false);
sweepPane.getParams(clickTypeProperty);
//need to refresh table to show symbol.
clickTypesTable.getTableView().refresh();
});
}

View File

@ -2,7 +2,9 @@ package matchedTemplateClassifer
;
import java.io.File;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import PamUtils.PamArrayUtils;
import PamUtils.TxtFileUtils;
@ -44,16 +46,21 @@ public class ImportTemplateCSV implements TemplateImport {
//System.out.println("i: " + i + " : " + data.get(0).get(i));
waveform[i]=data.get(0).get(i);
}
// System.out.println("String sR = " + data.get(1).get(0));
//used big decimal here because String.,floatValue did not handle numbers like 3.85e05
float sR = new BigDecimal(data.get(1).get(0)).floatValue();
float sR=data.get(1).get(0).floatValue();
// float sR=data.get(1).get(0).floatValue();
//System.out.println("imported waveform");
//PamArrayUtils.printArrayRaw(waveform);
//now create waveform
// System.out.println("Create a waveform with " + waveform.length + " samples with a sample rate of "
// + sR + " Hz");
System.out.println("Import a waveform with " + waveform.length + " samples with a sample rate of "
+ sR + " Hz ");
MatchTemplate matchedTemplate = new MatchTemplate(filePath.getName(), waveform, sR);
//TODO
return matchedTemplate;

View File

@ -129,7 +129,7 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters
if (fft==null) fft=new FastFFT();
//System.out.println("interpWaveform: " + waveformMatch.waveform.length + " sR " + waveformMatch.sR);
// System.out.println("interpWaveform: " + waveformMatch.waveform.length + " sR " + waveformMatch.sR);
//re-sample the waveform if the sample rate is different
this.interpWaveformMatch=interpWaveform(this.waveformMatch, sR);
@ -145,7 +145,12 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters
// System.out.println("MatchNorm: MATCH");
// MTClassifierTest.normalizeTest(interpWaveformMatch);
waveformMatchFFT = fft.rfft(interpWaveformMatch, length);
/**
* There is an issue here because, if we have a long template waveform, then it
* will become truncated and the actual waveform may be missed. This means we
* have to use the peak of the template
*/
waveformMatchFFT = calcTemplateFFT(interpWaveformMatch, length);
//need to calculate the complex conjugate - note that originally I was flipping the array but this means
//the max value does not equal one with identical waveforms...doh.
@ -157,6 +162,42 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters
return waveformMatchFFT;
}
/**
* Calculate the FFT of an interpolate match template.
* @param interpTemplateWaveform - the waveform interpolated to the correct sample rate.
* @param length - the length of the FFT.
* @return the FFT of the waveform as a complex array.
*/
private ComplexArray calcTemplateFFT(double[] interpTemplateWaveform, int length) {
ComplexArray fftTemplate;
/**
* There is an issue here because, if we have a long template waveform, then it
* will become truncated and the actual waveform may be missed. This means we
* have to use the peak of the template
*/
if (interpTemplateWaveform.length>length) {
//If the template is long then need to find the peak, otherwise we will end up cross correlating with noise at the
//start of the template.
//because this is a template and not a random click we don't need to be so clever with how we find peaks. Find
//the maximum and use around that.
int pos = PamArrayUtils.maxPos(interpTemplateWaveform);
int startind = Math.max(0, pos-length/2);
int endind = startind+length-1;
double[] peakTemplate = ArrayUtils.subarray(interpTemplateWaveform, startind, endind);
fftTemplate = fft.rfft(peakTemplate, length);
}
else {
//template waveform is padded by fft function
fftTemplate = fft.rfft(interpTemplateWaveform, length);
}
return fftTemplate;
}
/**
@ -181,13 +222,17 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters
// this.inteprWaveformReject=PamArrayUtils.divide(inteprWaveformReject, PamArrayUtils.max(inteprWaveformReject));
this.inteprWaveformReject = normaliseWaveform(inteprWaveformReject, this.normalisation);
// System.out.println("MatchNorm: REJECT ");
// MTClassifierTest.normalizeTest(inteprWaveformReject);
// MTClassifierTest.printWaveform(inteprWaveformReject);
//System.out.println("waveformReject: " +inteprWaveformReject.length + " fftLength: " + getFFTLength(sR));
waveformRejectFFT = fft.rfft(inteprWaveformReject, length);
/**
* There is an issue here because, if we have a long template waveform, then it
* will become truncated and the actual waveform may be missed. This means we
* have to use the peak of the template
*/
waveformRejectFFT = calcTemplateFFT(inteprWaveformReject, length);
//need to calculate the complex conjugate - note that originally I was flipping the array but this means
//the max value does not equal one with identical waveforms...doh.
@ -293,7 +338,7 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters
ComplexArray matchTemplate = getWaveformMatchFFT(sR, matchResult.length());
//System.out.println("Match template length: " + matchTemplate.length() + "Click : " + click.length());
// System.out.println("Match template length: " + matchTemplate.length() + "Click : " + click.length());
for (int i=0; i<matchTemplate.length(); i++) {
matchResult.set(i, click.get(i).times(matchTemplate.get(i)));
@ -376,7 +421,7 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters
//set the stored sR
currentSr=sR;
//System.out.println("Waveform click: len: " + click.length());
System.out.println("Waveform click: len: " + click.length());
//System.out.println("Waveform Reject max: " + PamArrayUtils.max(this.inteprWaveformReject)+ " len " + interpWaveformMatch.length);
@ -396,9 +441,11 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters
ComplexArray matchResult= new ComplexArray(fftLength);
ComplexArray matchTemplate = getWaveformMatchFFT(sR,fftLength); //remember this is the complex conjugate
//System.out.println("matchTemplate interp: len: " + interpWaveformMatch.length+ " max: " + PamArrayUtils.max(interpWaveformMatch));
//System.out.println("matchTemplate: len: " + waveformMatch.waveform.length+ " max: " + PamArrayUtils.max(waveformMatch.waveform));
// System.out.println("matchTemplate interp: len: " + interpWaveformMatch.length+ " max: " + PamArrayUtils.max(interpWaveformMatch));
// System.out.println("matchTemplate: len: " + waveformMatch.waveform.length+ " max: " + PamArrayUtils.max(waveformMatch.waveform));
// System.out.println("matchTemplate interp: len: " + interpWaveformMatch.length+ " max: " + PamArrayUtils.max(interpWaveformMatch));
System.out.println("matchTemplate len: " + matchTemplate.length() + " click.length(): " +click.length());
for (int i=0; i<Math.min(matchTemplate.length(), click.length()); i++) {
matchResult.set(i, click.get(i).times(matchTemplate.get(i)));
}
@ -409,13 +456,10 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters
rejectResult.set(i, click.get(i).times(rejectTemplate.get(i)));
}
// System.out.println("Waveform Match max: " + PamArrayUtils.max(this.interpWaveformMatch));
// System.out.println("Waveform Reject max: " + PamArrayUtils.max(this.inteprWaveformReject));
System.out.println("Waveform Match max: " + PamArrayUtils.max(this.interpWaveformMatch));
System.out.println("Waveform Reject max: " + PamArrayUtils.max(this.inteprWaveformReject));
//System.out.println("Click input: " + click.length() + " click template: " + matchTemplate.length());
//must use scaling to get the same result as MATLAB
if (fft==null) fft= new FastFFT();
@ -453,17 +497,18 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters
// //TEST
//if set to "none" then reject template will return a NaN - TODO bit messy and inefficient.
//if the reject template is set to "none" then reject template will return a NaN
//TODO bit messy and inefficient.
double result;
double maxReject = PamArrayUtils.max(rejectReal);
if (Double.isNaN(maxReject)) {
// double maxReject = PamArrayUtils.max(rejectReal);
if (Double.isNaN(maxreject)) {
result = PamArrayUtils.max(matchReal);
}
else {
result = PamArrayUtils.max(matchReal)-maxReject;
result = PamArrayUtils.max(matchReal)-maxreject;
}
//System.out.println("Match corr " + maxmatch + " Reject Corr: " + maxreject);
System.out.println("Match corr " + maxmatch + " Reject Corr: " + maxreject);
MatchedTemplateResult matchTmpResult = new MatchedTemplateResult();
matchTmpResult.threshold=result;
@ -476,17 +521,19 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters
/**
* Get the match waveform for the sample rate
* @param sR - the sample rate in samples per second
* @param waveformMatch - the template to interpolate or decimate.
* @param sR - the target sample rate in samples per second
*/
private double[] interpWaveform(MatchTemplate waveformMatch, double sR) {
//System.out.println("Interp waveform: " + " old: " + waveformMatch.sR + " new: " + sR);
// System.out.println("Interp waveform: " + " old: " + waveformMatch.sR + " new: " + sR);
if ( waveformMatch.sR>sR) {
if (waveformMatch.sR<sR) {
//up sample
double[] interpWaveformMatch=reSampleWaveform(waveformMatch.waveform, waveformMatch.sR, sR);
return interpWaveformMatch;
}
else if (waveformMatch.sR<sR){
else if (waveformMatch.sR>sR){
//decimate
// //TODO - make a better decimator?
// double[] interpWaveformMatch=reSampleWaveform(waveformMatch.waveform, waveformMatch.sR, sR);
// return interpWaveformMatch;
@ -494,11 +541,9 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters
return wavInterpolator.decimate(waveformMatch.waveform, waveformMatch.sR, (float) sR);
}
else {
//nothing needed/
//nothing needed
return waveformMatch.waveform;
}
}
}
@ -529,7 +574,8 @@ public class MTClassifier implements Serializable, Cloneable, ManagedParameters
// // TODO Auto-generated method stub
// return PamInterp.interpLinear(x, waveform, xi);
return PamInterp.interpWaveform(waveform, 1/binSize);
// System.out.println("Interp waveform: " + binSize);
return PamInterp.interpWaveform(waveform, 1./binSize);
}

View File

@ -5,6 +5,7 @@ import java.util.Arrays;
import PamController.PamController;
import PamDetection.RawDataUnit;
import PamUtils.PamArrayUtils;
import PamUtils.complex.ComplexArray;
import PamView.symbol.PamSymbolManager;
import PamguardMVC.PamDataBlock;
@ -288,6 +289,7 @@ public class MTProcess extends PamInstantProcess {
@SuppressWarnings("unused")
private double[] getWaveData(RawDataHolder clickDetection, int i) {
double[] waveform;
if (this.getMTParams().peakSearch) {
waveform = createRestrictedLenghtWave(clickDetection, i, lengthData[i],
this.getMTParams().restrictedBins);
@ -377,7 +379,14 @@ public class MTProcess extends PamInstantProcess {
*/
private double[] createRestrictedLenghtWave(RawDataHolder click, int chan, int[] lengthPoints,
int restrictedBins) {
return createRestrictedLenghtWave(click, chan, lengthPoints, restrictedBins, getWindow(restrictedBins));
// System.out.println("Create restricted length wave: " + lengthPoints[0] + " to " + lengthPoints[1]);
// System.out.println("Max before restrict: " + PamArrayUtils.max(click.getWaveData()[chan]));
double[] wave = createRestrictedLenghtWave(click, chan, lengthPoints, restrictedBins, getWindow(restrictedBins));
// System.out.println("Max after restrict: " + PamArrayUtils.max(click.getWaveData()[chan]));
return wave;
}
@ -412,7 +421,7 @@ public class MTProcess extends PamInstantProcess {
ArrayList<MatchedTemplateResult> results = new ArrayList<MatchedTemplateResult>();
//System.out.println("Click waveform max: " + PamArrayUtils.max(clickWaveform) + " sample rate: " + sR);
System.out.println("Click waveform max: " + PamArrayUtils.max(clickWaveform) + " sample rate: " + sR);
//normalisation and picking peak has already been performed

View File

@ -328,7 +328,7 @@ public class MTSettingsPane extends SettingsPane<MatchedTemplateParams> {
private Node createClassifierPane(){
//with just one classifier.
pamTabPane = new PamTabPane();
pamTabPane = new PamTabPane();
pamTabPane.setAddTabButton(true);
// pamTabPane.getAddTabButton().setGraphic(PamGlyphDude.createPamGlyph(MaterialIcon.ADD, PamGuiManagerFX.iconSize));
pamTabPane.getAddTabButton().setGraphic(PamGlyphDude.createPamIcon("mdi2p-plus", PamGuiManagerFX.iconSize));

View File

@ -40,19 +40,31 @@ public class MTClassifierOfflineTask extends OfflineTask<PamDataUnit<?,?>> {
@Override
public boolean processDataUnit(PamDataUnit dataUnit) {
count++;
mtClassifierControl.getMTProcess().newClickData(dataUnit);
try {
// System.out.println("MT new data unit: " + dataUnit);
count++;
mtClassifierControl.getMTProcess().newClickData(dataUnit);
//since an annotation has been added might need to do this so that the data unit is actually saved.
DataUnitFileInformation fileInfo = dataUnit.getDataUnitFileInformation();
// System.out.println("file info: " + fileInfo);
if (fileInfo != null) {
fileInfo.setNeedsUpdate(true);
}
dataUnit.updateDataUnit(System.currentTimeMillis());
return true;
//since an annotation has been added might need to do this so that the data unit is actually saved.
DataUnitFileInformation fileInfo = dataUnit.getDataUnitFileInformation();
//System.out.println("file info: " + fileInfo);
if (fileInfo != null) {
fileInfo.setNeedsUpdate(true);
}
dataUnit.updateDataUnit(System.currentTimeMillis());
return true;
catch (Exception e) {
e.printStackTrace();
}
return false;
}

View File

@ -96,10 +96,12 @@ public class PamTabPane extends TabPane {
}
/**
* TODO - the button is removed and then added again it does not seem to appear....
* Set whether a button shows to add tabs to the TabPane
* @param addTabButton - true to show a button next to the last tab which allows new tabs to be added.
*/
public void setAddTabButton(boolean addTabButton) {
if (this.addTabButton==addTabButton) return;
this.addTabButton = addTabButton;
}

View File

@ -1,17 +1,26 @@
package pamViewFX.fxNodes.flipPane;
import javafx.geometry.Insets;
import javafx.application.Platform;
import javafx.beans.value.ChangeListener;
import javafx.beans.value.ObservableValue;
import javafx.geometry.Orientation;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.Labeled;
import javafx.scene.control.TextField;
import javafx.scene.layout.HBox;
import javafx.scene.layout.Priority;
import javafx.scene.layout.Region;
import javafx.scene.paint.Color;
import javafx.scene.text.Text;
import javafx.scene.text.TextAlignment;
import pamViewFX.PamGuiManagerFX;
import pamViewFX.fxGlyphs.PamGlyphDude;
import pamViewFX.fxNodes.PamBorderPane;
import pamViewFX.fxNodes.PamButton;
import pamViewFX.fxNodes.PamHBox;
import pamViewFX.fxNodes.utilsFX.TextUtilsFX;
/**
* Flip pane which has is supposed to be used for advanced settings. The front
@ -32,15 +41,34 @@ public class PamFlipPane extends FlipPane {
private PamBorderPane frontPane;
private Label advLabel;
private PamButton backButton;
/**
* Text field in the title of the advanced pane. This can be used to change settings.
*/
private TextField advLabel;
/**
* Label which sits before the text field in the advanced settings pane title
*/
private Label preLabel;
/**
* Label after the the text field in the advanced pane label - this can be set to say "settings" for example with the text field
* then editable to change the name of a parameter.
*/
private Label postLabel;
public PamFlipPane() {
super();
this.advPane = createAdvSettingsPane();
this.getFront().getChildren().add(frontPane = new PamBorderPane());
// this.getFront().setStyle("-fx-background-color: grey;");
// this.getBack().setStyle("-fx-background-color: grey;");
this.getBack().getChildren().add(advPane);
this.setFlipTime(FLIP_TIME);
@ -77,6 +105,23 @@ public class PamFlipPane extends FlipPane {
public PamBorderPane getAdvPane() {
return advPane;
}
/**
* Set the advanced pane content.
* @param - the content to set.
*/
public void setAdvPaneContent(Node content) {
advPane.setCenter(content);
}
/**
* Set the front pane content.
* @param - the content to set.
*/
public void setFrontContent(Node content) {
frontPane.setCenter(content);
}
/**
@ -86,29 +131,70 @@ public class PamFlipPane extends FlipPane {
backButton = new PamButton();
backButton.setGraphic(PamGlyphDude.createPamIcon("mdi2c-chevron-left", Color.WHITE, PamGuiManagerFX.iconSize));
// backButton.setStyle("-fx-background-color: -color-base-6");
//backButton.setStyle("-fx-padding: 0,0,0,0");
backButton.setOnAction((action)->{
// System.out.println("FLIP BACK TO FRONT");
this.flipToFront();
});
//make the back button blue so users can easily see the button.
backButton.setStyle("-fx-background-radius: 0 5 5 0; -fx-border-radius: 0 5 5 0; -fx-background-color: -color-accent-6");
//backButton.setPrefWidth(150);
PamBorderPane advPane = new PamBorderPane();
advPane.setPadding(new Insets(5,5,5,5));
//advPane.setPadding(new Insets(5,5,5,5));
// holds the title of the advanced pane. This consists of a label for a graphic,
// an editable text field and a label after the editable settings field
PamHBox titleHolder = new PamHBox();
titleHolder.getChildren().addAll(preLabel = new Label(), advLabel = new TextField("Adv. "), postLabel = new Label("Settings"));
preLabel.setId("label-title2");
postLabel.setId("label-title2");
titleHolder.setAlignment(Pos.CENTER);
postLabel.setTextAlignment(TextAlignment.LEFT);
postLabel.setAlignment(Pos.CENTER_LEFT);
advLabel.setAlignment(Pos.CENTER);
// advLabel.prefColumnCountProperty().bind(advLabel.textProperty().length().subtract(3));
// Set Max and Min Width to PREF_SIZE so that the TextField is always PREF
advLabel.setMinWidth(Region.USE_PREF_SIZE);
advLabel.setMaxWidth(Region.USE_PREF_SIZE);
//pretty complicated to make sure the text field is the same size as the text that is being typed.
advLabel.textProperty().addListener((ov, prevText, currText) -> {
// Do this in a Platform.runLater because of Textfield has no padding at first time and so on
Platform.runLater(() -> {
Text text = new Text(currText);
text.setFont(advLabel.getFont()); // Set the same font, so the size is the same
double width = text.getLayoutBounds().getWidth() // This big is the Text in the TextField
+ advLabel.getPadding().getLeft() + advLabel.getPadding().getRight() // Add the padding of the TextField
+ 2d; // Add some spacing
advLabel.setPrefWidth(width); // Set the width
advLabel.positionCaret(advLabel.getCaretPosition()); // If you remove this line, it flashes a little bit
});
});
advLabel.setId("label-title2");
advLabel.setStyle("-fx-background-color: transparent");
titleHolder.setMaxWidth(Double.MAX_VALUE); //need to make sure label is in center.
//holds the back button and the title pane.
PamHBox buttonHolder = new PamHBox();
buttonHolder.setBackground(null);
//buttonHolder.setStyle("-fx-background-color: red;");
buttonHolder.setAlignment(Pos.CENTER_LEFT);
buttonHolder.getChildren().addAll(backButton, advLabel = new Label("Adv. Settings"));
buttonHolder.getChildren().addAll(backButton, titleHolder);
advLabel.setAlignment(Pos.CENTER);
advLabel.setMaxWidth(Double.MAX_VALUE); //need to make sure label is in center.
PamGuiManagerFX.titleFont2style(advLabel);
// PamGuiManagerFX.titleFont2style(advLabel);
advLabel.setAlignment(Pos.CENTER);
HBox.setHgrow(advLabel, Priority.ALWAYS);
HBox.setHgrow(titleHolder, Priority.ALWAYS);
advPane.setTop(buttonHolder);
@ -117,16 +203,45 @@ public class PamFlipPane extends FlipPane {
}
public Label getAdvLabel() {
public TextField getAdvLabel() {
return advLabel;
}
public void setAdvLabel(Label advLabel) {
this.advLabel = advLabel;
}
// public void setAdvLabel(Label advLabel) {
// this.advLabel = advLabel;
// }
public PamButton getBackButton() {
return backButton;
}
/**
* Get the label located before the editable label in the title
* @return the label before the editable label
*/
public Label getPreAdvLabel() {
return preLabel;
}
/**
* Get the label located after the editable label in the title
* @return the label after the editable label
*/
public Label getPostAdvLabel() {
return postLabel;
}
/**
* True if the flip pane is showing the front.
*/
public boolean isShowingFront() {
return super.flipFrontProperty().get();
}
public void setAdvLabelEditable(boolean b) {
this.advLabel.setEditable(b);
}
}

View File

@ -143,7 +143,7 @@ public class FilterPaneFX extends SettingsPane<FilterParams> {
}
else {
mainPane.setTop(createFilterPane());
mainPane.setBottom(createBodeGraph());
mainPane.setCenter(createBodeGraph());
}
}

View File

@ -1,19 +1,24 @@
package pamViewFX.fxNodes.utilityPanes;
import org.controlsfx.control.SegmentedButton;
import PamController.SettingsPane;
import PamUtils.LatLong;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Node;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.ToggleGroup;
import pamViewFX.fxNodes.PamBorderPane;
import javafx.scene.control.Toggle;
import javafx.scene.control.ToggleButton;
import javafx.scene.text.TextAlignment;
import pamViewFX.PamGuiManagerFX;
import pamViewFX.fxNodes.PamHBox;
import pamViewFX.fxNodes.PamVBox;
/**
* Pane with controls to to set the values for a Latitude and Longitude. The pane allows users to
* change the Latitude and Longitude in both Degrees, Decimal minutes and Degrees, Minutes, Seconds
*
* @author Jamie Macaulay
*
*/
@ -27,37 +32,62 @@ public class LatLongPane extends SettingsPane<LatLong>{
/**
* The radio button to select decimal minutes
*/
private RadioButton decimalMinutes;
private ToggleButton decimalMinutes;
/**
* Radio button to input minutes and seconds.
*/
private RadioButton minutesSeconds;
private ToggleButton minutesSeconds;
/**
* Lat long strip
*/
private LatLongStrip latStrip, longStrip;
private PamBorderPane mainPane;
private PamVBox mainPane;
private ToggleButton decimal;
/**
* Segmented button that also selection of the latitude and longitude format type.
*/
private SegmentedButton segmentedButton;
public LatLongPane(String title) {
super(null);
mainPane = new PamVBox();
mainPane.setSpacing(5);
mainPane.setAlignment(Pos.CENTER);
Label titleLabel = new Label(title);
titleLabel.maxWidth(Double.MAX_VALUE);
titleLabel.setTextAlignment(TextAlignment.LEFT);
PamGuiManagerFX.titleFont2style(titleLabel);
mainPane.getChildren().add(titleLabel);
latLong= new LatLong();
mainPane = new PamBorderPane();
decimalMinutes = new ToggleButton("Degrees, Decimal minutes");
minutesSeconds = new ToggleButton("Degrees, Minutes, Seconds");
decimal = new ToggleButton("Decimal");
segmentedButton = new SegmentedButton();
segmentedButton.getButtons().addAll(decimalMinutes, minutesSeconds, decimal);
PamHBox top = new PamHBox();
top.setSpacing(5);
top.getChildren().add(new Label("Unit type :"));
top.getChildren().add(decimalMinutes = new RadioButton("Degrees, Decimal minutes"));
top.getChildren().add(minutesSeconds = new RadioButton("Degrees, Minutes, Seconds"));
top.getChildren().add(segmentedButton);
ToggleGroup bg = new ToggleGroup();
decimalMinutes.setToggleGroup(bg);
minutesSeconds.setToggleGroup(bg);
// ToggleGroup bg = new ToggleGroup();
// decimalMinutes.setToggleGroup(bg);
// minutesSeconds.setToggleGroup(bg);
// decimal.setToggleGroup(bg);
decimalMinutes.setOnAction((action)->{
actionPerformed(action);
@ -66,7 +96,12 @@ public class LatLongPane extends SettingsPane<LatLong>{
minutesSeconds.setOnAction((action)->{
actionPerformed(action);
});
mainPane.setTop(top);
decimal.setOnAction((action)->{
actionPerformed(action);
});
mainPane.getChildren().add(top);
PamVBox cent = new PamVBox();
cent.setSpacing(5);
@ -75,8 +110,12 @@ public class LatLongPane extends SettingsPane<LatLong>{
cent.getChildren().add(latStrip = new LatLongStrip(true));
cent.getChildren().add(longStrip = new LatLongStrip(false));
mainPane.setCenter(cent);
//bit of a hack that makes sure controls are aligned for the latitude and longitude.
latStrip.getTitleLabel().prefWidthProperty().bind(longStrip.getTitleLabel().widthProperty());
mainPane.getChildren().add(cent);
decimal.setSelected(true);
}
@ -85,16 +124,17 @@ public class LatLongPane extends SettingsPane<LatLong>{
*/
public void actionPerformed(javafx.event.ActionEvent action) {
if (action.getSource() == decimalMinutes) {
LatLong.setFormatStyle(LatLong.FORMAT_DECIMALMINUTES);
// if (latStrip != null) {
// latStrip.setDecimalMinutes(true);
// longStrip.setDecimalMinutes(true);
// }
// if (latStrip != null) {
// latStrip.setDecimalMinutes(true);
// longStrip.setDecimalMinutes(true);
// }
latStrip.showControls(LatLong.FORMAT_DECIMALMINUTES);
longStrip.showControls(LatLong.FORMAT_DECIMALMINUTES);
}
else if (action.getSource() == minutesSeconds) {
else if (action.getSource() == minutesSeconds){
LatLong.setFormatStyle(LatLong.FORMAT_MINUTESSECONDS);
// if (latStrip != null) {
// latStrip.setDecimalMinutes(false);
@ -105,19 +145,26 @@ public class LatLongPane extends SettingsPane<LatLong>{
latStrip.showControls(LatLong.FORMAT_MINUTESSECONDS);
longStrip.showControls(LatLong.FORMAT_MINUTESSECONDS);
}
else if (action.getSource() == decimal){
LatLong.setFormatStyle(LatLong.FORMAT_DECIMAL);
latStrip.showControls(LatLong.FORMAT_DECIMAL);
longStrip.showControls(LatLong.FORMAT_DECIMAL);
}
}
private void showLatLong() {
decimalMinutes.setSelected(LatLong.getFormatStyle() == LatLong.FORMAT_DECIMALMINUTES);
minutesSeconds.setSelected(LatLong.getFormatStyle() == LatLong.FORMAT_MINUTESSECONDS);
latStrip.showControls(LatLong.getFormatStyle() );
longStrip.showControls(LatLong.getFormatStyle() );
latStrip.sayValue(latLong.getLatitude());
longStrip.sayValue(latLong.getLongitude());
decimalMinutes .setSelected(LatLong.getFormatStyle() == LatLong.FORMAT_DECIMALMINUTES);
minutesSeconds .setSelected(LatLong.getFormatStyle() == LatLong.FORMAT_MINUTESSECONDS);
decimal .setSelected(LatLong.getFormatStyle() == LatLong.FORMAT_DECIMAL);
latStrip .showControls(LatLong.getFormatStyle() );
longStrip .showControls(LatLong.getFormatStyle() );
latStrip .setValue(latLong.getLatitude());
longStrip .setValue(latLong.getLongitude());
}
@ -127,6 +174,21 @@ public class LatLongPane extends SettingsPane<LatLong>{
*/
@Override
public LatLong getParams(LatLong currentParams) {
Toggle selectedButton = this.segmentedButton.getToggleGroup().getSelectedToggle();
if (selectedButton == decimalMinutes) {
LatLong.setFormatStyle(LatLong.FORMAT_DECIMALMINUTES);
}
else if (selectedButton == minutesSeconds){
LatLong.setFormatStyle(LatLong.FORMAT_MINUTESSECONDS);
}
else if (selectedButton == decimal){
LatLong.setFormatStyle(LatLong.FORMAT_DECIMAL);
}
latLong = new LatLong(latStrip.getValue(), longStrip.getValue());
if (Double.isNaN(latLong.getLatitude()) || Double.isNaN(latLong.getLongitude())) {
return null;

View File

@ -17,24 +17,30 @@ import pamViewFX.fxNodes.PamHBox;
*
*/
public class LatLongStrip extends PamBorderPane {
Label formattedText;
TextField degrees, minutes, seconds, decminutes;
Label dl, ml, sl, dml;
TextField degrees, minutes, seconds, decminutes, decimal;
Label dl, ml, sl, dml, dec;
ComboBox<String> nsew;
boolean isLatitude;
// boolean decimalMinutes = true;
// boolean decimalMinutes = true;
/**
* HBox to hold decimal minutes, degrees, seconds controls.
*/
private PamHBox degHBox;
/**
* HBox to hold decimal controls
*/
private PamHBox decHBox;
/**
* The format type e.g. LatLong.FORMAT_DECIMALMINUTES.
*/
private int formatType = LatLong.FORMAT_DECIMALMINUTES;
private Label titleLabel;
/**
* Construct a strip of controls to include in a larger dialog.
@ -48,7 +54,7 @@ public class LatLongStrip extends PamBorderPane {
isLatitude = latitude;
createDialogStrip(showBorder);
}
/**
* Construct a strip of controls to include in a larger dialog.
* <p>By default the strip will have a titled border with the
@ -59,27 +65,33 @@ public class LatLongStrip extends PamBorderPane {
isLatitude = latitude;
createDialogStrip(true);
}
private void createDialogStrip(boolean showBorder) {
String borderTitle;
if (isLatitude) borderTitle = "Latitude";
else borderTitle = "Longitude";
Label title= new Label(borderTitle);
PamGuiManagerFX.titleFont2style(title);
// title.setFont(PamGuiManagerFX.titleFontSize2);
String title;
if (isLatitude) title = "Latitude";
else title = "Longitude";
// title.setFont(PamGuiManagerFX.titleFontSize2);
degrees = new TextField();
degrees.setEditable(true);
degrees.setPrefColumnCount(4);
minutes = new TextField();
minutes.setPrefColumnCount(3);
minutes.setEditable(true);
seconds = new TextField();
seconds.setPrefColumnCount(6);
seconds.setEditable(true);
decminutes = new TextField();
decminutes.setPrefColumnCount(6);
decminutes.setEditable(true);
decimal=new TextField();
decimal.setPrefColumnCount(9);
decimal.setEditable(true);
nsew = new ComboBox<String>();
nsew.setOnAction((action)->{
double v = getValue();
@ -91,6 +103,8 @@ public class LatLongStrip extends PamBorderPane {
ml = new Label("min.");
sl = new Label("sec.");
dml = new Label("dec min.");
dec = new Label("decimal deg.");
formattedText = new Label("Position");
if (isLatitude) {
nsew.getItems().add("N");
@ -100,123 +114,192 @@ public class LatLongStrip extends PamBorderPane {
nsew.getItems().add("E");
nsew.getItems().add("W");
}
degrees.setOnKeyPressed((key)->{
newTypedValues(key);
});
minutes.setOnKeyPressed((key)->{
newTypedValues(key);
});
seconds.setOnKeyPressed((key)->{
newTypedValues(key);
});
decminutes.setOnKeyPressed((key)->{
newTypedValues(key);
});
decimal.setOnKeyPressed((key)->{
newTypedValues(key);
});
nsew.setOnKeyPressed((key)->{
newTypedValues(key);
});
degHBox = new PamHBox();
degHBox.setSpacing(5);
degHBox.setAlignment(Pos.CENTER_LEFT);
this.setRight(nsew);
this.setCenter(degHBox);
PamHBox holder= new PamHBox();
holder.setAlignment(Pos.CENTER_LEFT);
holder.setSpacing(5);
holder.getChildren().addAll(titleLabel = new Label(title), degHBox, nsew);
this.setCenter(holder);
this.setBottom(formattedText);
showControls( LatLong.FORMAT_DECIMALMINUTES);
showControls(formatType);
}
private void newTypedValues(KeyEvent e) {
double v = getValue();
// now need to put that into the fields that
// are not currently shown so that they are
// ready if needed.
if (e != null) {
sayValue(v, true);
}
// // now need to put that into the fields that
// // are not currently shown so that they are
// // ready if needed.
//
// if (e != null) {
// setValue(v, true);
// }
// and say the formated version
sayFormattedValue(v);
}
public void showControls(int formatStyle) {
boolean decimal = (formatStyle == LatLong.FORMAT_DECIMALMINUTES);
if (formatType==formatStyle) {
return;
}
//important this comes before setting format style.
double currentValue = getValue();
this.formatType = formatStyle;
degHBox.getChildren().clear();
if (decimal) {
System.out.println("FORMATSTYLE: " + formatStyle + " val " + currentValue);
switch (formatType) {
case LatLong.FORMAT_DECIMALMINUTES:
degHBox.getChildren().add(dl);
degHBox.getChildren().add(degrees);
degHBox.getChildren().add(dml);
degHBox.getChildren().add(decminutes);
}
else {
break;
case LatLong.FORMAT_MINUTESSECONDS:
degHBox.getChildren().add(dl);
degHBox.getChildren().add(degrees);
degHBox.getChildren().add(ml);
degHBox.getChildren().add(minutes);
degHBox.getChildren().add(sl);
degHBox.getChildren().add(seconds);
break;
case LatLong.FORMAT_DECIMAL:
degHBox.getChildren().add(dec);
degHBox.getChildren().add(decimal);
break;
}
minutes.setVisible(decimal == false);
ml.setVisible(decimal == false);
seconds.setVisible(decimal == false);
sl.setVisible(decimal == false);
decminutes.setVisible(decimal);
dml.setVisible(decimal);
setValue(currentValue);
sayFormattedValue(getValue());
}
/**
* Set data in the lat long dialog strip
* @param value Lat or Long in decimal degrees.
*/
public void sayValue(double value) {
sayValue(value, false);
public void setValue(double value) {
setValue(value, false);
}
public void sayValue(double value, boolean hiddenOnly) {
public void setValue(double value, boolean hiddenOnly) {
System.out.println("Set value: " + value);
if (value >= 0) {
nsew.getSelectionModel().select(0);
}
else {
nsew.getSelectionModel().select(1);
}
double deg = LatLong.getSignedDegrees(value);
// System.out.println("Deg: " + LatLong.getSignedDegrees(value) + " value: " +value);
if (degrees.isVisible() == false || !hiddenOnly) degrees.setText(String.format("%d", (int)Math.abs(deg)));
if (minutes.isVisible() == false || !hiddenOnly) minutes.setText(String.format("%d", LatLong.getIntegerMinutes(value)));
if (decminutes.isVisible() == false || !hiddenOnly) decminutes.setText(String.format("%3.5f", LatLong.getDecimalMinutes(value)));
if (seconds.isVisible() == false || !hiddenOnly) seconds.setText(String.format("%3.5f", LatLong.getSeconds(value)));
if (nsew.isVisible() == false || !hiddenOnly) nsew.getSelectionModel().select(deg >= 0 ? 0 : 1);
// System.out.println("Deg: " + LatLong.getSignedDegrees(value) + " value: " +value);
// if (degrees.isVisible() == false || !hiddenOnly) degrees.setText(String.format("%d", (int)Math.abs(deg)));
// if (minutes.isVisible() == false || !hiddenOnly) minutes.setText(String.format("%d", LatLong.getIntegerMinutes(value)));
// if (decminutes.isVisible() == false || !hiddenOnly) decminutes.setText(String.format("%3.5f", LatLong.getDecimalMinutes(value)));
// if (seconds.isVisible() == false || !hiddenOnly) seconds.setText(String.format("%3.5f", LatLong.getSeconds(value)));
// if (nsew.isVisible() == false || !hiddenOnly) nsew.getSelectionModel().select(deg >= 0 ? 0 : 1);
// if (decimal.isVisible() == false || !hiddenOnly) decimal.setText(String.format("%.8f", value));
switch (formatType) {
case LatLong.FORMAT_DECIMALMINUTES:
degrees.setText(String.format("%d", (int)Math.abs(deg)));
decminutes.setText(String.format("%3.5f", LatLong.getDecimalMinutes(value)));
break;
case LatLong.FORMAT_MINUTESSECONDS:
degrees.setText(String.format("%d", (int)Math.abs(deg)));
minutes.setText(String.format("%d", LatLong.getIntegerMinutes(value)));
seconds.setText(String.format("%3.5f", LatLong.getSeconds(value)));
break;
case LatLong.FORMAT_DECIMAL:
decimal.setText(String.format("%.8f", value));
break;
}
sayFormattedValue(value);
}
/**
* Get the value for the latitude and longitude
* @return the value.
* Get the value for the latitude or longitude in decimal
* @return the value - the value in decimal
*/
public double getValue() {
double deg = 0;
double min = 0;
double sec = 0;
double sin = 1.;
if (nsew.getSelectionModel().getSelectedIndex()== 1) sin = -1.;
if (nsew.getSelectionModel().getSelectedIndex() == 1) sin = -1.;
if (formatType == LatLong.FORMAT_DECIMAL){
try {
deg = Double.valueOf(decimal.getText());
return deg;
}
catch (NumberFormatException ex) {
return Double.NaN;
}
}
try {
deg = Integer.valueOf(degrees.getText());
deg = Integer.valueOf(degrees.getText());
}
catch (NumberFormatException Ex) {
return Double.NaN;
}
if (LatLong.getFormatStyle() == LatLong.FORMAT_DECIMALMINUTES){
if (formatType == LatLong.FORMAT_DECIMALMINUTES){
try {
min = Double.valueOf(decminutes.getText());
}
@ -237,12 +320,13 @@ public class LatLongStrip extends PamBorderPane {
catch (NumberFormatException ex) {
return Double.NaN;
}
}
deg += min/60 + sec/3600;
deg *= sin;
return deg;
}
/**
* Clear the latitude/longitude data from the pane.
*/
@ -251,9 +335,10 @@ public class LatLongStrip extends PamBorderPane {
minutes.setText("");
seconds.setText("");
decminutes.setText("");
decimal.setText("");
}
public void sayFormattedValue(double value) {
if (isLatitude) {
formattedText.setText(LatLong.formatLatitude(value));
@ -262,13 +347,13 @@ public class LatLongStrip extends PamBorderPane {
formattedText.setText(LatLong.formatLongitude(value));
}
}
// public boolean isDecimalMinutes() {
// return decimalMinutes;
// }
// public void setDecimalMinutes(boolean decimalMinutes) {
// this.decimalMinutes = decimalMinutes;
// showControls();
// }
// public boolean isDecimalMinutes() {
// return decimalMinutes;
// }
// public void setDecimalMinutes(boolean decimalMinutes) {
// this.decimalMinutes = decimalMinutes;
// showControls();
// }
/**
* Set the pane to be enabled or disabled.
@ -281,5 +366,16 @@ public class LatLongStrip extends PamBorderPane {
seconds.setDisable(!enabled);
decminutes.setDisable(!enabled);
nsew.setDisable(!enabled);
decimal.setDisable(!enabled);
}
/**
* Get the title label.
* @return the title label.
*/
public Label getTitleLabel() {
return titleLabel;
}
}

View File

@ -0,0 +1,40 @@
package pamViewFX.fxNodes.utilsFX;
import javafx.scene.text.Font;
import javafx.scene.text.Text;
import javafx.scene.text.TextBoundsType;
public class TextUtilsFX {
static final Text helper;
static final double DEFAULT_WRAPPING_WIDTH;
static final double DEFAULT_LINE_SPACING;
static final String DEFAULT_TEXT;
static final TextBoundsType DEFAULT_BOUNDS_TYPE;
static {
helper = new Text();
DEFAULT_WRAPPING_WIDTH = helper.getWrappingWidth();
DEFAULT_LINE_SPACING = helper.getLineSpacing();
DEFAULT_TEXT = helper.getText();
DEFAULT_BOUNDS_TYPE = helper.getBoundsType();
}
public static double computeTextWidth(Font font, String text, double help0) {
// Toolkit.getToolkit().getFontLoader().computeStringWidth(field.getText(),
// field.getFont());
helper.setText(text);
helper.setFont(font);
helper.setWrappingWidth(0.0D);
helper.setLineSpacing(0.0D);
double d = Math.min(helper.prefWidth(-1.0D), help0);
helper.setWrappingWidth((int) Math.ceil(d));
d = Math.ceil(helper.getLayoutBounds().getWidth());
helper.setWrappingWidth(DEFAULT_WRAPPING_WIDTH);
helper.setLineSpacing(DEFAULT_LINE_SPACING);
helper.setText(DEFAULT_TEXT);
return d;
}
}