From 60b76fb71115a3d3c13834941c0bab87c900edb3 Mon Sep 17 00:00:00 2001 From: Jamie Mac Date: Thu, 14 Dec 2023 12:06:51 +0000 Subject: [PATCH] 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! --- .classpath | 3 +- src/Array/layoutFX/InterpChoicePane.java | 2 +- src/Array/layoutFX/StreamerSettingsPane.java | 25 +++-- src/Array/layoutFX/StreamersPane.java | 2 +- src/PamUtils/PamArrayUtils.java | 76 ++++++++++--- src/PamUtils/TxtFileUtils.java | 19 +++- src/clickDetector/ClickBTDisplay.java | 2 +- .../BasicIdentifierPaneFX.java | 2 + .../clickClassifiers/ClickClassifyPaneFX.java | 2 +- .../SweepClassifierPaneFX.java | 8 +- .../ImportTemplateCSV.java | 13 ++- .../MTClassifier.java | 102 +++++++++++++----- src/matchedTemplateClassifer/MTProcess.java | 13 ++- .../layoutFX/MTSettingsPane.java | 2 +- .../offline/MTClassifierOfflineTask.java | 34 ++++-- src/pamViewFX/fxNodes/PamTabPane.java | 2 + .../fxNodes/flipPane/PamFlipPane.java | 41 ++++++- .../fxNodes/utilityPanes/FilterPaneFX.java | 2 +- .../fxNodes/utilityPanes/LatLongPane.java | 40 +++++-- .../fxNodes/utilityPanes/LatLongStrip.java | 49 ++++++--- .../fxNodes/utilsFX/TextUtilsFX.java | 40 +++++++ 21 files changed, 372 insertions(+), 107 deletions(-) create mode 100644 src/pamViewFX/fxNodes/utilsFX/TextUtilsFX.java diff --git a/.classpath b/.classpath index ba93b2e0..188c7291 100644 --- a/.classpath +++ b/.classpath @@ -6,9 +6,8 @@ - + - diff --git a/src/Array/layoutFX/InterpChoicePane.java b/src/Array/layoutFX/InterpChoicePane.java index 590efc5d..b44b534b 100644 --- a/src/Array/layoutFX/InterpChoicePane.java +++ b/src/Array/layoutFX/InterpChoicePane.java @@ -23,7 +23,7 @@ public class InterpChoicePane extends InterpSettingsPane { interpChoiceBox = new ChoiceBox(); interpChoiceBox.getItems().addAll(interpChoice); - + interpChoiceBox.setMaxWidth(Double.MAX_VALUE); interpChoiceBox.setConverter(new StringConverter<>() { @Override diff --git a/src/Array/layoutFX/StreamerSettingsPane.java b/src/Array/layoutFX/StreamerSettingsPane.java index b1663434..1107990a 100644 --- a/src/Array/layoutFX/StreamerSettingsPane.java +++ b/src/Array/layoutFX/StreamerSettingsPane.java @@ -490,14 +490,14 @@ public class StreamerSettingsPane extends SettingsPane { defaultStreamer.setDz(Double.valueOf(zPosErr.getText())); } catch (NumberFormatException e) { - System.err.println("There is a problem with one of the position parameters in the streamer panel"); + System.err.println("Streamer getParams: There is a problem with one of the position parameters in the streamer panel"); return null; } defaultStreamer.setStreamerName(currParams.getStreamerName()); int im = interpPane.getSelection(); if (im < 0) { - System.err.println("There is a problem with the interpolation selection streamer panel"); + System.err.println("Streamer getParams: There is an index problem with the interpolation selection streamer panel: index = " + im); } currentArray.setOriginInterpolation(im); // try { @@ -516,7 +516,7 @@ public class StreamerSettingsPane extends SettingsPane { // } // masterLocator.setHydrophoneLocator(streamerIndex, locator); if (currentOriginMethod == null) { - System.err.println("No hydrophoneorigin method selected in streamer panel"); + System.err.println("Streamer getParams: No hydrophoneorigin method selected in streamer panel"); } OriginDialogComponent mthDialogComponent = currentOriginMethod.getDialogComponent(); if (mthDialogComponent != null) { @@ -532,13 +532,13 @@ public class StreamerSettingsPane extends SettingsPane { defaultStreamer.setRoll(getDoubleValue(roll)); // } if (!heading.isDisable() && defaultStreamer.getHeading() == null) { - System.err.println("You must enter a fixed value for the streamer heading"); + System.err.println("Streamer getParams: You must enter a fixed value for the streamer heading"); } if (!pitch.isDisable() && defaultStreamer.getPitch() == null) { - System.err.println("You must enter a fixed value for the streamer pitch"); + System.err.println("Streamer getParams: You must enter a fixed value for the streamer pitch"); } if (!roll.isDisable() && defaultStreamer.getRoll() == null) { - System.err.println("You must enter a fixed value for the streamer roll"); + System.err.println("Streamer getParams: You must enter a fixed value for the streamer roll"); } @@ -563,13 +563,13 @@ public class StreamerSettingsPane extends SettingsPane { } } - return currParams; + return defaultStreamer; } @Override public void setParams(Streamer input) { if (input==null) { - System.out.print("The input streamer is null"); + System.out.print("Streamer setParams: The input streamer is null"); } this.defaultStreamer=input; // origin methods @@ -616,21 +616,24 @@ public class StreamerSettingsPane extends SettingsPane { OriginDialogComponent mthDialogComponent = mth.getDialogComponent(); if (mthDialogComponent != null) { + System.out.println("Streamer setParams: Set origin component: "); mthDialogComponent.setParams(); } - System.out.println("Set orientation: " + defaultStreamer.getHeading() + " " + defaultStreamer.getPitch() + " " + defaultStreamer.getRoll()); + System.out.println("Streamer setParams: Set orientation: " + defaultStreamer.getHeading() + " " + defaultStreamer.getPitch() + " " + defaultStreamer.getRoll()); heading .setText(orientation2Text(defaultStreamer.getHeading())); pitch .setText(orientation2Text(defaultStreamer.getPitch())); roll .setText(orientation2Text(defaultStreamer.getRoll())); - System.out.println("Origin interpolator: " + currentArray.getOriginInterpolation()); + System.out.println("Streamer setParams: Origin interpolator: " + currentArray.getOriginInterpolation()); if (currentArray.getOriginInterpolation()<0) { interpPane.setSelection(0); } - else interpPane.setSelection(currentArray.getOriginInterpolation()); + else { + interpPane.setSelection(currentArray.getOriginInterpolation()); + } ArraySensorFieldType[] sensorFields = ArraySensorFieldType.values(); for (int i = 0; i < sensorFields.length; i++) { diff --git a/src/Array/layoutFX/StreamersPane.java b/src/Array/layoutFX/StreamersPane.java index 5719892e..c921df8b 100644 --- a/src/Array/layoutFX/StreamersPane.java +++ b/src/Array/layoutFX/StreamersPane.java @@ -55,7 +55,7 @@ public class StreamersPane extends PamBorderPane { this.setCenter(tableArrayPane); pamFlipePane = new PamFlipPane(); - pamFlipePane.getAdvLabel().setText("Hydrophone Settings"); + pamFlipePane.getAdvLabel().setText("Streamer"); ((Pane) streamerPane.getContentNode()).setPadding(new Insets(5,5,5,15)); diff --git a/src/PamUtils/PamArrayUtils.java b/src/PamUtils/PamArrayUtils.java index 3fa34a5f..9d6b0730 100644 --- a/src/PamUtils/PamArrayUtils.java +++ b/src/PamUtils/PamArrayUtils.java @@ -449,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 @@ -476,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 @@ -490,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= 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; diff --git a/src/clickDetector/layoutFX/clickClassifiers/BasicIdentifierPaneFX.java b/src/clickDetector/layoutFX/clickClassifiers/BasicIdentifierPaneFX.java index becb7380..e2cf0f39 100644 --- a/src/clickDetector/layoutFX/clickClassifiers/BasicIdentifierPaneFX.java +++ b/src/clickDetector/layoutFX/clickClassifiers/BasicIdentifierPaneFX.java @@ -6,6 +6,7 @@ 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; @@ -108,6 +109,7 @@ public class BasicIdentifierPaneFX implements ClassifyPaneFX { protected Node createSettingsPane(){ mainHolderPane=new PamBorderPane(); + mainHolderPane.setPadding(new Insets(5,5,5,5)); mainHolderPane.setCenter(clickTypesTable=new ClickClassifierTable(clickClassifiers)); clickTypeHolder=new PamBorderPane(); diff --git a/src/clickDetector/layoutFX/clickClassifiers/ClickClassifyPaneFX.java b/src/clickDetector/layoutFX/clickClassifiers/ClickClassifyPaneFX.java index c71d55bd..cbc16c86 100644 --- a/src/clickDetector/layoutFX/clickClassifiers/ClickClassifyPaneFX.java +++ b/src/clickDetector/layoutFX/clickClassifiers/ClickClassifyPaneFX.java @@ -110,7 +110,7 @@ public class ClickClassifyPaneFX extends PamStackPane { //create the main pane. PamVBox holderPane=new PamVBox(); - // holderPane.setPadding(new Insets(5,5,5,5)); + holderPane.setPadding(new Insets(5,5,5,5)); holderPane.setSpacing(5); //create label diff --git a/src/clickDetector/layoutFX/clickClassifiers/SweepClassifierPaneFX.java b/src/clickDetector/layoutFX/clickClassifiers/SweepClassifierPaneFX.java index b099e835..a300684c 100644 --- a/src/clickDetector/layoutFX/clickClassifiers/SweepClassifierPaneFX.java +++ b/src/clickDetector/layoutFX/clickClassifiers/SweepClassifierPaneFX.java @@ -8,7 +8,9 @@ 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. @@ -42,14 +44,16 @@ 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().getPreAdvLabel().graphicProperty().bind(sweepPane.getNameGraphicProperty()); - sweepPane.classifierItemRow = sweepClickClassifier.getSweepClassifierParams().getSetRow((SweepClassifierSet) clickTypeProperty.getClickType()); sweepPane.setParams(clickTypeProperty); diff --git a/src/matchedTemplateClassifer/ImportTemplateCSV.java b/src/matchedTemplateClassifer/ImportTemplateCSV.java index 0713f026..69b1063e 100644 --- a/src/matchedTemplateClassifer/ImportTemplateCSV.java +++ b/src/matchedTemplateClassifer/ImportTemplateCSV.java @@ -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; diff --git a/src/matchedTemplateClassifer/MTClassifier.java b/src/matchedTemplateClassifer/MTClassifier.java index 476d4735..ddde2bc7 100644 --- a/src/matchedTemplateClassifer/MTClassifier.java +++ b/src/matchedTemplateClassifer/MTClassifier.java @@ -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; isR) { + if (waveformMatch.sRsR){ + //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); } diff --git a/src/matchedTemplateClassifer/MTProcess.java b/src/matchedTemplateClassifer/MTProcess.java index 32c46040..92e86a2f 100644 --- a/src/matchedTemplateClassifer/MTProcess.java +++ b/src/matchedTemplateClassifer/MTProcess.java @@ -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 results = new ArrayList(); - //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 diff --git a/src/matchedTemplateClassifer/layoutFX/MTSettingsPane.java b/src/matchedTemplateClassifer/layoutFX/MTSettingsPane.java index 2ca0748e..c9c545f6 100644 --- a/src/matchedTemplateClassifer/layoutFX/MTSettingsPane.java +++ b/src/matchedTemplateClassifer/layoutFX/MTSettingsPane.java @@ -328,7 +328,7 @@ public class MTSettingsPane extends SettingsPane { 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)); diff --git a/src/matchedTemplateClassifer/offline/MTClassifierOfflineTask.java b/src/matchedTemplateClassifer/offline/MTClassifierOfflineTask.java index 99b9ca68..f2544265 100644 --- a/src/matchedTemplateClassifer/offline/MTClassifierOfflineTask.java +++ b/src/matchedTemplateClassifer/offline/MTClassifierOfflineTask.java @@ -40,19 +40,31 @@ public class MTClassifierOfflineTask extends OfflineTask> { @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; + } diff --git a/src/pamViewFX/fxNodes/PamTabPane.java b/src/pamViewFX/fxNodes/PamTabPane.java index 261740d8..14ab8ab0 100644 --- a/src/pamViewFX/fxNodes/PamTabPane.java +++ b/src/pamViewFX/fxNodes/PamTabPane.java @@ -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; tabPaneSkin = new PamTabPaneSkin(this); } diff --git a/src/pamViewFX/fxNodes/flipPane/PamFlipPane.java b/src/pamViewFX/fxNodes/flipPane/PamFlipPane.java index 43530920..c5a12aaf 100644 --- a/src/pamViewFX/fxNodes/flipPane/PamFlipPane.java +++ b/src/pamViewFX/fxNodes/flipPane/PamFlipPane.java @@ -1,5 +1,8 @@ package pamViewFX.fxNodes.flipPane; +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; @@ -8,12 +11,16 @@ 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 @@ -146,22 +153,48 @@ public class PamFlipPane extends FlipPane { 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 TextField("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); - advLabel.setId("label-title2"); - advLabel.setStyle("-fx-background-color: transparent"); + advLabel.setAlignment(Pos.CENTER); - HBox.setHgrow(advLabel, Priority.ALWAYS); + HBox.setHgrow(titleHolder, Priority.ALWAYS); advPane.setTop(buttonHolder); diff --git a/src/pamViewFX/fxNodes/utilityPanes/FilterPaneFX.java b/src/pamViewFX/fxNodes/utilityPanes/FilterPaneFX.java index c758442f..33eb11c1 100644 --- a/src/pamViewFX/fxNodes/utilityPanes/FilterPaneFX.java +++ b/src/pamViewFX/fxNodes/utilityPanes/FilterPaneFX.java @@ -143,7 +143,7 @@ public class FilterPaneFX extends SettingsPane { } else { mainPane.setTop(createFilterPane()); - mainPane.setBottom(createBodeGraph()); + mainPane.setCenter(createBodeGraph()); } } diff --git a/src/pamViewFX/fxNodes/utilityPanes/LatLongPane.java b/src/pamViewFX/fxNodes/utilityPanes/LatLongPane.java index b80f2539..711491dd 100644 --- a/src/pamViewFX/fxNodes/utilityPanes/LatLongPane.java +++ b/src/pamViewFX/fxNodes/utilityPanes/LatLongPane.java @@ -8,6 +8,7 @@ import javafx.geometry.Insets; import javafx.geometry.Pos; import javafx.scene.Node; import javafx.scene.control.Label; +import javafx.scene.control.Toggle; import javafx.scene.control.ToggleButton; import javafx.scene.text.TextAlignment; import pamViewFX.PamGuiManagerFX; @@ -47,6 +48,11 @@ public class LatLongPane extends SettingsPane{ private ToggleButton decimal; + /** + * Segmented button that also selection of the latitude and longitude format type. + */ + private SegmentedButton segmentedButton; + public LatLongPane(String title) { super(null); @@ -71,7 +77,7 @@ public class LatLongPane extends SettingsPane{ minutesSeconds = new ToggleButton("Degrees, Minutes, Seconds"); decimal = new ToggleButton("Decimal"); - SegmentedButton segmentedButton = new SegmentedButton(); + segmentedButton = new SegmentedButton(); segmentedButton.getButtons().addAll(decimalMinutes, minutesSeconds, decimal); PamHBox top = new PamHBox(); @@ -109,6 +115,7 @@ public class LatLongPane extends SettingsPane{ mainPane.getChildren().add(cent); + decimal.setSelected(true); } @@ -144,18 +151,20 @@ public class LatLongPane extends SettingsPane{ 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.setValue(latLong.getLatitude()); - longStrip.setValue(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()); } @@ -165,6 +174,21 @@ public class LatLongPane extends SettingsPane{ */ @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; diff --git a/src/pamViewFX/fxNodes/utilityPanes/LatLongStrip.java b/src/pamViewFX/fxNodes/utilityPanes/LatLongStrip.java index d8a3f368..2e80e8f0 100644 --- a/src/pamViewFX/fxNodes/utilityPanes/LatLongStrip.java +++ b/src/pamViewFX/fxNodes/utilityPanes/LatLongStrip.java @@ -75,17 +75,22 @@ public class LatLongStrip extends PamBorderPane { // 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(); nsew.setOnAction((action)->{ @@ -152,25 +157,35 @@ public class LatLongStrip extends PamBorderPane { 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) { - setValue(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) { + + if (formatType==formatStyle) { + return; + } + + //important this comes before setting format style. + double currentValue = getValue(); this.formatType = formatStyle; degHBox.getChildren().clear(); + + System.out.println("FORMATSTYLE: " + formatStyle + " val " + currentValue); - System.out.println("FORMATSTYLE: " + formatStyle); switch (formatType) { case LatLong.FORMAT_DECIMALMINUTES: degHBox.getChildren().add(dl); @@ -192,7 +207,9 @@ public class LatLongStrip extends PamBorderPane { break; } - + + setValue(currentValue); + sayFormattedValue(getValue()); } @@ -205,6 +222,8 @@ public class LatLongStrip extends PamBorderPane { } public void setValue(double value, boolean hiddenOnly) { + + System.out.println("Set value: " + value); if (value >= 0) { nsew.getSelectionModel().select(0); } @@ -249,18 +268,20 @@ public class LatLongStrip extends PamBorderPane { } /** - * 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 (LatLong.getFormatStyle() == LatLong.FORMAT_DECIMAL){ + if (formatType == LatLong.FORMAT_DECIMAL){ try { deg = Double.valueOf(decimal.getText()); return deg; @@ -278,7 +299,7 @@ public class LatLongStrip extends PamBorderPane { } - if (LatLong.getFormatStyle() == LatLong.FORMAT_DECIMALMINUTES){ + if (formatType == LatLong.FORMAT_DECIMALMINUTES){ try { min = Double.valueOf(decminutes.getText()); } @@ -355,4 +376,6 @@ public class LatLongStrip extends PamBorderPane { public Label getTitleLabel() { return titleLabel; } + + } diff --git a/src/pamViewFX/fxNodes/utilsFX/TextUtilsFX.java b/src/pamViewFX/fxNodes/utilsFX/TextUtilsFX.java new file mode 100644 index 00000000..d47098d7 --- /dev/null +++ b/src/pamViewFX/fxNodes/utilsFX/TextUtilsFX.java @@ -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; + } +} \ No newline at end of file