Merge from DG (#145)

* Localization output

* update localiser output
This commit is contained in:
Douglas Gillespie 2024-08-08 14:27:05 +01:00 committed by GitHub
parent adf4c87781
commit bea4a544d0
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
32 changed files with 721 additions and 177 deletions

View File

@ -1,5 +1,7 @@
package Localiser;
import java.io.Serializable;
import PamDetection.LocContents;
import PamDetection.LocalisationInfo;
@ -12,10 +14,9 @@ import PamDetection.LocalisationInfo;
public interface LocalisationAlgorithm {
/**
* Get the likely content flags for this localiser.
* @see LocalisationInfo
* @see LocContents
* @return localisation flags.
* Get information about the localisation algorithm.
* @return algorithm information.
*/
public int getLocalisationContents();
public LocalisationAlgorithmInfo getAlgorithmInfo();
}

View File

@ -0,0 +1,31 @@
package Localiser;
import java.io.Serializable;
import PamDetection.LocContents;
import PamDetection.LocalisationInfo;
public interface LocalisationAlgorithmInfo {
/**
* Get the likely content flags for this localiser.
* @see LocalisationInfo
* @see LocContents
* @return localisation flags.
*/
public int getLocalisationContents();
/**
* Get the algorithm name
* @return algorithm name
*/
public String getAlgorithmName();
/**
* Get the algorithm parameters. Something else
* can turn these into xml for Tethys.
* @return algorithm parameters object. Might be null;
*/
public Serializable getParameters();
}

View File

@ -1,6 +1,7 @@
package Localiser.algorithms.timeDelayLocalisers.bearingLoc;
import Localiser.LocalisationAlgorithm;
import Localiser.LocalisationAlgorithmInfo;
import pamMaths.PamVector;
/**
@ -9,7 +10,7 @@ import pamMaths.PamVector;
* @author Doug Gillespie
*
*/
public interface BearingLocaliser extends LocalisationAlgorithm {
public interface BearingLocaliser extends LocalisationAlgorithm, LocalisationAlgorithmInfo {
/**
* Do any preparation necessary (e.g. creation of look up tables)

View File

@ -1,8 +1,12 @@
package Localiser.algorithms.timeDelayLocalisers.bearingLoc;
import java.io.Serializable;
import Localiser.LocalisationAlgorithmInfo;
import PamDetection.LocContents;
import pamMaths.PamVector;
@Deprecated
public class CombinedBearingLocaliser implements BearingLocaliser {
private BearingLocaliser firstLocaliser;
@ -67,4 +71,19 @@ public class CombinedBearingLocaliser implements BearingLocaliser {
return res2;
}
@Override
public String getAlgorithmName() {
return "Combined Simplex bearing localiser";
}
@Override
public Serializable getParameters() {
return null;
}
@Override
public LocalisationAlgorithmInfo getAlgorithmInfo() {
return this;
}
}

View File

@ -1,10 +1,13 @@
package Localiser.algorithms.timeDelayLocalisers.bearingLoc;
import java.io.Serializable;
import Array.ArrayManager;
import Array.PamArray;
import Jama.LUDecomposition;
import Jama.Matrix;
import Jama.QRDecomposition;
import Localiser.LocalisationAlgorithmInfo;
import PamDetection.LocContents;
import PamUtils.PamUtils;
import pamMaths.PamVector;
@ -77,12 +80,12 @@ public class LSQBearingLocaliser implements BearingLocaliser {
weightedHydrophoneVectors.set(iRow, e, v.getElement(e)/c*fitWeights[iRow]);
hydrophoneVectors.set(iRow, e, v.getElement(e)/c);
hydrophoneErrorVectors.set(iRow, e, errorVec.getElement(e)/c);
// hydrophoneUnitVectors.set(iRow, e, uv.getElement(e));
// hydrophoneUnitVectors.set(iRow, e, uv.getElement(e));
}
iRow++;
}
}
// luHydrophoneUnitMatrix = new LUDecomposition(hydrophoneUnitVectors);
// luHydrophoneUnitMatrix = new LUDecomposition(hydrophoneUnitVectors);
qrHydrophones = new QRDecomposition(weightedHydrophoneVectors);
}
@ -118,21 +121,21 @@ public class LSQBearingLocaliser implements BearingLocaliser {
@Override
public double[][] localise(double[] delays, long timeMillis) {
resetArray(timeMillis);
// qrHydrophones = new QRDecomposition(hydrophoneVectors);
// qrHydrophones = new QRDecomposition(hydrophoneVectors);
Matrix normDelays = new Matrix(delays.length, 1);
for (int i = 0; i < delays.length; i++) {
normDelays.set(i, 0, -delays[i]*fitWeights[i]);
}
// Matrix soln = luHydrophoneUnitMatrix.solve(normDelays);
// Matrix soln = luHydrophoneUnitMatrix.solve(normDelays);
Matrix soln2 = qrHydrophones.solve(normDelays);
double[][] angs = new double[2][2];
PamVector v = new PamVector(soln2.get(0, 0), soln2.get(1,0), soln2.get(2, 0));
// System.out.printf("Vector Norm = %4.3f: ", v.norm());
// System.out.printf("Vector Norm = %4.3f: ", v.norm());
double m = v.normalise();
angs[0][0] = Math.PI/2. - Math.atan2(v.getElement(0),v.getElement(1));
angs[0][1] = Math.asin(v.getElement(2));
// timingError = 1e-5;
// timingError = 1e-5;
// now take a look at angle errors
double oneDeg = Math.PI/180.;
@ -149,7 +152,7 @@ public class LSQBearingLocaliser implements BearingLocaliser {
l3 = logLikelihood(delays, angs[0][0] + aDiff, angs[0][1]);
er[0][i] = angs[1][0] = Math.sqrt(1./(l1+l3-2*l2))*aDiff;
l1a = logLikelihood(delays, angs[0][0], angs[0][1] - aDiff);
// l2 = logLikelihood(delays, angs[0][0], angs[0][1]);
// l2 = logLikelihood(delays, angs[0][0], angs[0][1]);
l3a = logLikelihood(delays, angs[0][0], angs[0][1] + aDiff);
er[1][i] = angs[1][1] = Math.sqrt(1./(l1a+l3a-2*l2))*aDiff;
}
@ -157,14 +160,14 @@ public class LSQBearingLocaliser implements BearingLocaliser {
// double ll[] = new double[21];
// double a[] = new double[2];
// timingError = 1.e-4;
// a[1] = angs[0][1];
// for (int i = 0; i < ll.length; i++) {
// a[0] = angs[0][0] + (-10 + i)*oneDeg;
// ll[i] = logLikelihood(delays, a);
// }
// double ll[] = new double[21];
// double a[] = new double[2];
// timingError = 1.e-4;
// a[1] = angs[0][1];
// for (int i = 0; i < ll.length; i++) {
// a[0] = angs[0][0] + (-10 + i)*oneDeg;
// ll[i] = logLikelihood(delays, a);
// }
return angs;
}
/**
@ -190,11 +193,11 @@ public class LSQBearingLocaliser implements BearingLocaliser {
*/
public double logLikelihood(double[] delays, double[] angles) {
return logLikelihood(delays, angles[0], angles[1]);
// Matrix whaleVec = new Matrix(3,1);
// whaleVec.set(0, 0, Math.cos(angles[1])*Math.cos(angles[0]));
// whaleVec.set(1, 0, Math.cos(angles[1])*Math.sin(angles[0]));
// whaleVec.set(2, 0, Math.sin(angles[1]));
// return logLikelihood(delays, whaleVec);
// Matrix whaleVec = new Matrix(3,1);
// whaleVec.set(0, 0, Math.cos(angles[1])*Math.cos(angles[0]));
// whaleVec.set(1, 0, Math.cos(angles[1])*Math.sin(angles[0]));
// whaleVec.set(2, 0, Math.sin(angles[1]));
// return logLikelihood(delays, whaleVec);
}
/**
* Calculate a log likelihood for a given whale vector.
@ -216,4 +219,17 @@ public class LSQBearingLocaliser implements BearingLocaliser {
return chi/2;
}
@Override
public String getAlgorithmName() {
return "Least Squares bearing localiser";
}
@Override
public Serializable getParameters() {
return null;
}
@Override
public LocalisationAlgorithmInfo getAlgorithmInfo() {
return this;
}
}

View File

@ -1,10 +1,12 @@
package Localiser.algorithms.timeDelayLocalisers.bearingLoc;
import java.io.Serializable;
import java.util.Arrays;
import Array.ArrayManager;
import Array.PamArray;
import Jama.Matrix;
import Localiser.LocalisationAlgorithmInfo;
import Localiser.algorithms.Correlations;
import PamDetection.LocContents;
import PamUtils.ArrayDump;
@ -636,5 +638,18 @@ public class MLGridBearingLocaliser implements BearingLocaliser {
return hydrophoneMap;
}
@Override
public String getAlgorithmName() {
return "Maximum likelyhood grid bearing localiser";
}
@Override
public Serializable getParameters() {
return null;
}
@Override
public LocalisationAlgorithmInfo getAlgorithmInfo() {
return this;
}
}

View File

@ -1,10 +1,12 @@
package Localiser.algorithms.timeDelayLocalisers.bearingLoc;
import java.io.Serializable;
import java.util.Arrays;
import Array.ArrayManager;
import Array.PamArray;
import Jama.Matrix;
import Localiser.LocalisationAlgorithmInfo;
import Localiser.algorithms.Correlations;
import Localiser.algorithms.PeakPosition2D;
import Localiser.algorithms.PeakSearch;
@ -13,6 +15,7 @@ import PamUtils.ArrayDump;
import PamUtils.PamUtils;
import PamUtils.SystemTiming;
import pamMaths.PamVector;
import tethys.pamdata.AutoTethysProvider;
/**
* Revamp of the earlier MLGridBearingLocaliser but with a more sensible
@ -744,4 +747,35 @@ public class MLGridBearingLocaliser2 implements BearingLocaliser {
return likelihoodLUT;
}
@Override
public String getAlgorithmName() {
return "Maximum likelyhood grid bearing localiser V2";
}
@Override
public Serializable getParameters() {
return new LocaliserParams(this.thetaStep, this.phiStep);
}
@Override
public LocalisationAlgorithmInfo getAlgorithmInfo() {
return this;
}
/**
* For passing to Tethys output.
* @author dg50
*
*/
public class LocaliserParams implements Serializable {
private static final long serialVersionUID = 1L;
public double thetaStep, phiStep;
public LocaliserParams(double thetaStep, double phiStep) {
super();
this.thetaStep = Math.toDegrees(thetaStep);
this.phiStep = Math.toDegrees(phiStep);
this.thetaStep = AutoTethysProvider.roundDecimalPlaces(this.thetaStep, 2);
this.phiStep = AutoTethysProvider.roundDecimalPlaces(this.phiStep, 2);
}
}
}

View File

@ -1,11 +1,13 @@
package Localiser.algorithms.timeDelayLocalisers.bearingLoc;
import java.io.Serializable;
import java.util.Arrays;
import Array.ArrayManager;
import Array.PamArray;
import Jama.Matrix;
import Jama.QRDecomposition;
import Localiser.LocalisationAlgorithmInfo;
import PamDetection.LocContents;
import PamUtils.PamUtils;
import pamMaths.PamVector;
@ -294,4 +296,17 @@ public class PairBearingLocaliser implements BearingLocaliser {
return speedOfSound;
}
@Override
public String getAlgorithmName() {
return "Pair bearing localiser";
}
@Override
public Serializable getParameters() {
return null;
}
@Override
public LocalisationAlgorithmInfo getAlgorithmInfo() {
return this;
}
}

View File

@ -1,5 +1,6 @@
package Localiser.algorithms.timeDelayLocalisers.bearingLoc;
import java.io.Serializable;
import java.util.Arrays;
import org.apache.commons.math.FunctionEvaluationException;
@ -14,10 +15,12 @@ import Array.ArrayManager;
import Array.PamArray;
import Jama.Matrix;
import Jama.QRDecomposition;
import Localiser.LocalisationAlgorithmInfo;
import PamDetection.LocContents;
import PamUtils.PamUtils;
import pamMaths.PamVector;
@Deprecated
public class SimplexBearingLocaliser implements BearingLocaliser {
private int arrayType;
private Matrix hydrophoneVectors;
@ -237,6 +240,20 @@ public class SimplexBearingLocaliser implements BearingLocaliser {
this.firstStep = firstStep;
}
@Override
public String getAlgorithmName() {
return "Simplex bearing localiser";
}
@Override
public LocalisationAlgorithmInfo getAlgorithmInfo() {
return this;
}
@Override
public Serializable getParameters() {
return null;
}
// private class BearingConvergence implements RealConvergenceChecker {
//
// @Override

View File

@ -49,6 +49,7 @@ import org.w3c.dom.Element;
import Acquisition.AcquisitionControl;
import Acquisition.AcquisitionProcess;
import Localiser.LocalisationAlgorithm;
import pamScrollSystem.ViewLoadObserver;
import tethys.TethysControl;
import tethys.pamdata.AutoTethysProvider;
@ -2531,6 +2532,42 @@ public class PamDataBlock<Tunit extends PamDataUnit> extends PamObservable {
this.localisationContents.addLocContent(localisationContents);
}
/**
* Find localisation algorithm for this data.
* This may be within the owning module, or a downstream algorithm.
* @return first found localisation algorithm or null;
*/
public LocalisationAlgorithm getLocalisationAlgorithm() {
/*
* first check downstream modules. If these are in use, they
* probably override anything internal, so look for these first.
*/
List<PamObserver> instObs = getInstantObservers();
for (PamObserver obs : instObs) {
if (obs instanceof LocalisationAlgorithm) {
return (LocalisationAlgorithm) obs;
}
if (obs instanceof PamProcess) {
PamProcess proc = (PamProcess) obs;
PamControlledUnit pcu = proc.getPamControlledUnit();
if (pcu == null) {
continue;
}
if (pcu instanceof LocalisationAlgorithm) {
return (LocalisationAlgorithm) pcu;
}
}
}
// if nothing downstream, then check the owning module
PamProcess proc = getParentProcess();
if (proc == null) return null;
PamControlledUnit pcu = proc.getPamControlledUnit();
if (pcu instanceof LocalisationAlgorithm) {
return (LocalisationAlgorithm) pcu;
}
return null;
}
public static final int ITERATOR_END = -1;
/**

View File

@ -562,6 +562,20 @@ public class PamObservable {//extends PanelOverlayDraw {
return PamModel.getPamModel().getPamModelSettings().getThreadingJitterMillis();
}
/**
* @return the pamObservers
*/
protected List<PamObserver> getPamObservers() {
return pamObservers;
}
/**
* @return the instantObservers
*/
protected List<PamObserver> getInstantObservers() {
return instantObservers;
}
// @Override
// public boolean hasOptionsDialog(GeneralProjector generalProjector) {
// if (overlayDraw != null) {

View File

@ -10,14 +10,18 @@ import java.util.List;
import javax.swing.JMenu;
import javax.swing.JMenuItem;
import Localiser.LocalisationAlgorithm;
import Localiser.LocalisationAlgorithmInfo;
import PamController.PamControlledUnit;
import PamController.PamControlledUnitSettings;
import PamController.PamController;
import PamController.PamSettingManager;
import PamController.PamSettings;
import PamDetection.LocContents;
import PamUtils.SimpleObservable;
import PamguardMVC.PamDataUnit;
import beamformer.algorithms.BeamAlgorithmProvider;
import bearinglocaliser.algorithms.BearingAlgorithm;
import bearinglocaliser.algorithms.BearingAlgorithmProvider;
import bearinglocaliser.annotation.BearingAnnotationType;
import bearinglocaliser.beamformer.BeamFormBearingWrapper;
@ -30,7 +34,7 @@ import offlineProcessing.OfflineTaskGroup;
import pamViewFX.fxNodes.pamDialogFX.PamDialogFX2AWT;
import userDisplay.UserDisplayControl;
public class BearingLocaliserControl extends PamControlledUnit implements PamSettings {
public class BearingLocaliserControl extends PamControlledUnit implements PamSettings, LocalisationAlgorithm, LocalisationAlgorithmInfo {
public static final String unitType = "Bearing Calculator";
@ -228,4 +232,46 @@ public class BearingLocaliserControl extends PamControlledUnit implements PamSet
public String getHelpPoint() {
return helpPoint;
}
@Override
public LocalisationAlgorithmInfo getAlgorithmInfo() {
return this;
}
private BearingAlgorithm findAlgorithm() {
BearingAlgorithmGroup[] groups = bearingProcess.getBearingAlgorithmGroups();
if (groups == null) {
return null;
}
for (int i = 0; i < groups.length; i++) {
BearingAlgorithm ba = groups[i].bearingAlgorithm;
if (ba != null) {
return ba;
}
}
return null;
}
@Override
public int getLocalisationContents() {
int cont = LocContents.HAS_BEARING | LocContents.HAS_BEARINGERROR;
// work out if we should also add ambiguity. How to work that out ?
return cont;
}
@Override
public String getAlgorithmName() {
// BearingAlgorithm ba = findAlgorithm();
// if (ba == null) {
// return null;
// }
// ba.getParams().
return getUnitType();
}
@Override
public Serializable getParameters() {
return bearingLocaliserParams;
}
}

View File

@ -44,6 +44,8 @@ import targetMotionOld.TargetMotionLocaliser;
import binaryFileStorage.BinaryStore;
import Filters.FilterDialog;
import Filters.FilterParams;
import Localiser.LocalisationAlgorithm;
import Localiser.LocalisationAlgorithmInfo;
import Localiser.detectionGroupLocaliser.GroupDetection;
import PamController.PamConfiguration;
import PamController.PamControlledUnit;
@ -112,7 +114,7 @@ import offlineProcessing.OfflineTaskGroup;
*
*/
public class ClickControl extends PamControlledUnit implements PamSettings {
public class ClickControl extends PamControlledUnit implements PamSettings, LocalisationAlgorithm {
protected ClickDetector clickDetector;
@ -1293,5 +1295,10 @@ public class ClickControl extends PamControlledUnit implements PamSettings {
return clickFFTDataOrganiser;
}
@Override
public LocalisationAlgorithmInfo getAlgorithmInfo() {
return clickDetector.getLocaliserInfo();
}
}

View File

@ -42,6 +42,7 @@ import Filters.Filter;
import Filters.FilterMethod;
import Filters.FilterType;
import Localiser.DelayMeasurementParams;
import Localiser.LocalisationAlgorithmInfo;
import Localiser.algorithms.Correlations;
import Localiser.algorithms.DelayGroup;
import Localiser.algorithms.TimeDelayData;
@ -2109,4 +2110,22 @@ public class ClickDetector extends PamProcess {
super.destroyProcess();
newClickMonitor.destroyProcess();
}
/**
* Get information about the internal bearing localiser. Will have to do
* just for the first sub detector.
* @return
*/
public LocalisationAlgorithmInfo getLocaliserInfo() {
if (channelGroupDetectors == null || channelGroupDetectors.length == 0) {
return null;
}
BearingLocaliser bl = channelGroupDetectors[0].bearingLocaliser;
if (bl == null) {
return null;
}
else {
return bl.getAlgorithmInfo();
}
}
}

View File

@ -31,6 +31,7 @@ public class ClickEventTethysDataProvider extends AutoTethysProvider {
GranularityEnumType[] allowed = {GranularityEnumType.GROUPED};
return allowed;
}
@Override
public Detection createDetection(PamDataUnit dataUnit, TethysExportParams tethysExportParams,
StreamExportParams streamExportParams) {

View File

@ -1,22 +1,20 @@
package rawDeepLearningClassifier.tethys;
import java.io.Serializable;
import java.util.ArrayList;
import javax.xml.bind.JAXBException;
import PamguardMVC.PamDataBlock;
import PamguardMVC.PamDataUnit;
import nilus.Detection;
import nilus.Detection.Parameters;
import rawDeepLearningClassifier.DLControl;
import rawDeepLearningClassifier.RawDLParams;
import rawDeepLearningClassifier.dlClassification.DLClassiferModel;
import rawDeepLearningClassifier.dlClassification.DLDetection;
import rawDeepLearningClassifier.dlClassification.PredictionResult;
import rawDeepLearningClassifier.logging.DLAnnotation;
import tethys.TethysControl;
import tethys.output.StreamExportParams;
import tethys.output.TethysExportParams;
import tethys.pamdata.AutoTethysProvider;
import tethys.pamdata.TethysParameterPacker;
public class DLTethysDataProvider extends AutoTethysProvider {
@ -36,6 +34,26 @@ public class DLTethysDataProvider extends AutoTethysProvider {
}
DLDetection dlDetection = (DLDetection) dataUnit;
// try to find the score which is burried in the annotation
DLAnnotation annotation = (DLAnnotation) dlDetection.findDataAnnotation(DLAnnotation.class) ;
if (annotation != null) {
double bestScore = 0;
ArrayList<PredictionResult> results = annotation.getModelResults();
for (PredictionResult res : results) {
float[] resres = res.getPrediction();
if (resres != null) for (int i = 0; i < resres.length; i++) {
double aRes = resres[i];
if (aRes > bestScore) {
bestScore = aRes;
}
}
}
bestScore = roundSignificantFigures(bestScore, 4);
detection.getParameters().setScore(bestScore);
}
// ds = getPamDataBlock().
// result =
String annotSummary = dlDetection.getAnnotationsSummaryString();
if (annotSummary != null) {

View File

@ -570,16 +570,22 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet
for (DatablockSynchInfo synchInfo : dataBlockSynchInfos) {
// dataPrefixes[i] = DetectionsHandler.getDetectionsDocIdPrefix(deplData.getProject(), synchInfo.getDataBlock());
int detectionCount = 0;
int documentCount = 0;
int locDocumentCount = 0;
int detDocumentCount = 0;
for (PDeployment pDepl : matchedDeployments) {
detectionCount += dbxmlQueries.countData(synchInfo.getDataBlock(), pDepl.getDocumentId());
ArrayList<String> detectionsNames = getDbxmlQueries().getDetectionsDocuments(synchInfo.getDataBlock(), pDepl.getDocumentId());
if (detectionsNames != null) {
documentCount += detectionsNames.size();
detDocumentCount += detectionsNames.size();
}
ArrayList<String> locDocNames = getDbxmlQueries().getLocalizationDocuments(synchInfo.getDataBlock(), pDepl.getDocumentId());
if (locDocNames != null) {
locDocumentCount += locDocNames.size();
}
}
synchInfo.setDataCount(detectionCount);
synchInfo.setDetectionDocumentCount(documentCount);
synchInfo.setDetectionDocumentCount(detDocumentCount);
synchInfo.setLocalizationDocumentCount(locDocumentCount);
i++;
}

View File

@ -10,6 +10,8 @@ import javax.swing.SwingWorker;
import javax.xml.datatype.DatatypeConstants;
import javax.xml.datatype.XMLGregorianCalendar;
import Localiser.LocalisationAlgorithm;
import Localiser.LocalisationAlgorithmInfo;
import PamController.PamControlledUnit;
import PamController.PamController;
import PamController.PamguardVersionInfo;
@ -665,7 +667,7 @@ public class DetectionsHandler extends CollectionHandler {
AlgorithmType algorithm = detections.getAlgorithm();
if (dataProvider != null) {
algorithm = dataProvider.getAlgorithm();
algorithm = dataProvider.getAlgorithm(Collection.Detections);
// detections.setAlgorithm(algorithm);
}
algorithm.setMethod(getMethodString(dataBlock));
@ -750,10 +752,20 @@ public class DetectionsHandler extends CollectionHandler {
AlgorithmType algorithm = localisations.getAlgorithm();
if (dataProvider != null) {
algorithm = dataProvider.getAlgorithm();
algorithm = dataProvider.getAlgorithm(Collection.Localizations);
// detections.setAlgorithm(algorithm);
}
LocalisationAlgorithm locAlgorithm = dataBlock.getLocalisationAlgorithm();
LocalisationAlgorithmInfo locAlgoinfo = null;
if (locAlgorithm != null) {
locAlgoinfo = locAlgorithm.getAlgorithmInfo();
}
if (locAlgoinfo != null) {
algorithm.setMethod(locAlgoinfo.getAlgorithmName());
}
else {
algorithm.setMethod(getMethodString(dataBlock));
}
algorithm.setSoftware(getSoftwareString(dataBlock));
algorithm.setVersion(getVersionString(dataBlock));

View File

@ -1,7 +1,5 @@
package tethys.detection;
import java.util.List;
import java.util.ListIterator;
import javax.xml.datatype.DatatypeConstants;
import javax.xml.datatype.XMLGregorianCalendar;
@ -9,7 +7,6 @@ import javax.xml.datatype.XMLGregorianCalendar;
import PamguardMVC.PamDataBlock;
import PamguardMVC.PamDataUnit;
import nilus.Detection;
import nilus.DetectionGroup;
import nilus.Detections;
import nilus.GranularityEnumType;
import tethys.TethysControl;

View File

@ -3,6 +3,8 @@ package tethys.localization;
import java.math.BigInteger;
import java.util.ArrayList;
import Localiser.detectionGroupLocaliser.GroupLocResult;
import Localiser.detectionGroupLocaliser.GroupLocalisation;
import PamDetection.AbstractLocalisation;
import PamDetection.LocContents;
import PamUtils.LatLong;
@ -17,7 +19,8 @@ import nilus.LocalizationType;
import nilus.LocalizationType.References;
import nilus.LocalizationType.References.Reference;
import nilus.Localize;
import nilus.SpeciesIDType
import nilus.SpeciesIDType;
import pamMaths.PamVector;
import nilus.Localize.Effort.CoordinateReferenceSystem;
import tethys.Collection;
import tethys.CollectionHandler;
@ -37,13 +40,13 @@ public class LocalizationHandler extends CollectionHandler {
super(tethysControl, Collection.Localizations);
}
// public LocalizationType getLoc() {
// LocalizationType lt = new LocalizationType();
// CylindricalCoordinateType cct = new CylindricalCoordinateType();
//// cct.set
// CoordinateReferenceSystem cr;
// return null;
// }
// public LocalizationType getLoc() {
// LocalizationType lt = new LocalizationType();
// CylindricalCoordinateType cct = new CylindricalCoordinateType();
//// cct.set
// CoordinateReferenceSystem cr;
// return null;
// }
/**
* Get a list of Localization documents associated with a particular data block for all deployments
@ -72,14 +75,14 @@ public class LocalizationHandler extends CollectionHandler {
if (someNames == null) {
continue;
}
// // no have a list of all the Detections documents of interest for this datablock.
// // no have a list of all the Detections documents of interest for this datablock.
for (String aDoc : someNames) {
Localize localize = tethysControl.getDbxmlQueries().getLocalizationDocInfo(aDoc);
int count = tethysControl.getDbxmlQueries().countLocalizations2(aDoc);
PLocalization pLocalize = new PLocalization(localize, dataBlock, aDep, count);
localizeDocs.add(pLocalize);
// PDetections pDetections = new PDetections(detections, dataBlock, aDep, count);
// detectionsDocs.add(pDetections);
// PDetections pDetections = new PDetections(detections, dataBlock, aDep, count);
// detectionsDocs.add(pDetections);
}
}
return new StreamDetectionsSummary(localizeDocs);
@ -194,6 +197,22 @@ public class LocalizationHandler extends CollectionHandler {
return deg;
}
/**
* Convert a vertical angle from radians to degrees and round.
* @param radians
* @return
*/
private double toSlantAngle(double radians) {
/*
* these really need to be constrained to -90 to 90, but I don't see what to do if
* they are outside that range.
*/
double deg = Math.toDegrees(radians);
deg= PamUtils.constrainedAngle(deg, 180);
deg = AutoTethysProvider.roundDecimalPlaces(deg, 2);
return deg;
}
private LocalizationType createWGS84Loc(Localize localiseDocument, PamDataBlock dataBlock, PamDataUnit dataUnit,
StreamExportParams streamExportParams) {
AbstractLocalisation loc = dataUnit.getLocalisation();
@ -210,8 +229,31 @@ public class LocalizationHandler extends CollectionHandler {
coord.setY(latlong.getLatitude());
coord.setZ(latlong.getHeight());
PamVector planarVec = loc.getPlanarVector();
locType.setCoordinate(coord);
// see if it's possible to get a beam measurement.
if (loc instanceof GroupLocalisation) {
GroupLocalisation groupLoc = (GroupLocalisation) loc;
GroupLocResult groupLocResult = groupLoc.getGroupLocaResult(0);
Double perpDist = groupLocResult.getPerpendicularDistance();
Long beamTime = groupLocResult.getBeamTime();
if (perpDist != null && beamTime != null) {
AngularCoordinateType acType = new AngularCoordinateType();
acType.setAngle1(90);
acType.setDistanceM(AutoTethysProvider.roundDecimalPlaces(perpDist,1));
locType.setAngularCoordinate(acType);
locType.setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(beamTime));
locType.setCoordinate(null);
}
// groupLoc.getp
}
/*
* Try to also add a range loc.
*/
// loc.
return locType;
}
@ -241,7 +283,10 @@ public class LocalizationHandler extends CollectionHandler {
BearingType angType = new BearingType();
angType.setAngle1(constrainRadianAngle(angles[0]));
if (angles.length >= 2) {
angType.setAngle2(constrainRadianAngle(angles[1]));
angType.setAngle2(toSlantAngle(angles[1]));
// if (angType.getAngle2() > 360) {
// angType.setAngle2(Math.toDegrees(angles[1]));
// }
}
locType.setBearing(angType);
@ -277,6 +322,9 @@ public class LocalizationHandler extends CollectionHandler {
angType.setAngle1(constrainRadianAngle(angles[0]));
if (angles.length >= 2) {
angType.setAngle2(constrainRadianAngle(angles[1]));
if (angType.getAngle2() > 360) {
angType.setAngle2(toSlantAngle(angles[1]));
}
}
if (loc.hasLocContent(LocContents.HAS_RANGE)) {
angType.setDistanceM(loc.getRange(0));

View File

@ -28,6 +28,8 @@ public class DatablockSynchInfo {
*/
private int detectionDocumentCount;
private int localizationDocumentCount;
public DatablockSynchInfo(TethysControl tethysControl, PamDataBlock dataBlock) {
super();
@ -69,4 +71,18 @@ public class DatablockSynchInfo {
this.detectionDocumentCount = detectionDocumentCount;
}
/**
* @return the localizationDocumentCount
*/
public int getLocalizationDocumentCount() {
return localizationDocumentCount;
}
/**
* @param localizationDocumentCount the localizationDocumentCount to set
*/
public void setLocalizationDocumentCount(int localizationDocumentCount) {
this.localizationDocumentCount = localizationDocumentCount;
}
}

View File

@ -79,6 +79,9 @@ public class StreamExportParams implements Serializable {
if (detectionDescription == null) {
detectionDescription = new WrappedDescriptionType();
}
// if (detectionDescription.getMethod() == null) {
//
// }
return detectionDescription;
}
@ -88,6 +91,29 @@ public class StreamExportParams implements Serializable {
autoFill(tethysControl, dataBlock);
}
/**
* Used to get the description data back in again if it's changes
* as PAMGuard updates. This object can't store references to the
* TethysControl or the datablock since they aren't serializable.
* Normally, the description is auto filled at constructoin, but once
* serialized, this can no longer happen, so can call this function to
* sort it all out.
*/
public void checkDescription() {
TethysControl tethysControl = (TethysControl) PamController.getInstance().findControlledUnit(TethysControl.class, null);
PamDataBlock dataBlock = PamController.getInstance().getDataBlockByLongName(longDataName);
if (tethysControl == null || dataBlock == null) {
return; // probably impossible for this to happen, but just in case.
}
DescriptionType desc = getNilusDetectionDescription();
if (desc == null) {
detectionDescription.setDescription(desc = new DescriptionType());
}
if (desc.getMethod() == null || desc.getMethod().length() == 0) {
autoFill(tethysControl, dataBlock);
}
}
/**
* Try to put some information automatically into the Methods.
* @param dataBlock2

View File

@ -9,6 +9,8 @@ import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import Localiser.LocalisationAlgorithm;
import Localiser.LocalisationAlgorithmInfo;
import PamController.PamControlledUnit;
import PamController.PamSettings;
import PamController.PamguardVersionInfo;
@ -35,6 +37,7 @@ import nilus.DetectionEffortKind;
import nilus.GranularityEnumType;
import nilus.Helper;
import nilus.SpeciesIDType;
import tethys.Collection;
import tethys.TethysControl;
import tethys.TethysTimeFuncs;
import tethys.detection.DetectionsHandler;
@ -101,7 +104,7 @@ abstract public class AutoTethysProvider implements TethysDataProvider {
}
@Override
public AlgorithmType getAlgorithm() {
public AlgorithmType getAlgorithm(Collection collection) {
/**
* Probably need to split this to provide detection algorithm parameters and
* localisation algorithm parameters, or pass in the document type as a function
@ -113,14 +116,64 @@ abstract public class AutoTethysProvider implements TethysDataProvider {
} catch (IllegalArgumentException | IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
// do the parameters as normal whether it's dets or locs.
nilus.AlgorithmType.Parameters algoParameters = this.getAlgorithmParameters();
if (algoParameters != null) {
algorithm.setParameters(algoParameters);
}
if (collection == Collection.Localizations) {
nilus.AlgorithmType.Parameters locParameters = this.getLocalisationParameters();
if (algoParameters == null) {
algorithm.setParameters(locParameters);
}
else if (locParameters != null) {
// merge the two sets, putting the localisation information first.
List<Element> mainList = algoParameters.getAny();
List<Element> locList = locParameters.getAny();
if (mainList != null && locList != null) {
for (int i = 0; i < locList.size(); i++) {
mainList.add(i, locList.get(i));
}
}
}
}
else {
}
return algorithm;
}
public nilus.AlgorithmType.Parameters getLocalisationParameters() {
LocalisationAlgorithm algo = pamDataBlock.getLocalisationAlgorithm();
if (algo == null) {
return null;
}
LocalisationAlgorithmInfo algoInfo = algo.getAlgorithmInfo();
if (algoInfo == null) {
return null;
}
Object params = algoInfo.getParameters();
if (params == null) {
return null;
}
// pack the params object
TethysParameterPacker paramPacker = null;
try {
paramPacker = new TethysParameterPacker(tethysControl);
} catch (JAXBException e) {
e.printStackTrace();
}
Element paramEl = paramPacker.packObject(params, "localizer");
if (paramEl == null) {
return null;
}
nilus.AlgorithmType.Parameters parameters = new nilus.AlgorithmType.Parameters();
List<Element> paramList = parameters.getAny();
paramList.add(paramEl);
return parameters;
}
@Override
public nilus.AlgorithmType.Parameters getAlgorithmParameters() {
if (pamControlledUnit instanceof PamSettings == false) {
@ -537,6 +590,13 @@ abstract public class AutoTethysProvider implements TethysDataProvider {
}
}
/**
* @return the pamDataBlock
*/
protected PamDataBlock getPamDataBlock() {
return pamDataBlock;
}
}

View File

@ -11,6 +11,7 @@ import nilus.DescriptionType;
import nilus.Detection;
import nilus.DetectionEffortKind;
import nilus.GranularityEnumType;
import tethys.Collection;
import tethys.localization.TethysLocalisationInfo;
import tethys.niluswraps.PDeployment;
import tethys.output.StreamExportParams;
@ -62,9 +63,10 @@ public interface TethysDataProvider {
/**
* Get Algorithm information for a Tethys Detections document
* @param collection Detections or Localisations may have different parameter sets.
* @return Algorithm information
*/
public AlgorithmType getAlgorithm();
public AlgorithmType getAlgorithm(Collection collection);
/**
* Get a list of allowed granularity types for this output

View File

@ -24,6 +24,7 @@ import PamController.settings.output.xml.PamguardXMLWriter;
import PamModel.parametermanager.ManagedParameters;
import PamModel.parametermanager.PamParameterData;
import PamModel.parametermanager.PamParameterSet;
import PamModel.parametermanager.SimplePamParameterData;
import PamguardMVC.PamDataBlock;
import PamguardMVC.PamProcess;
import PamguardMVC.dataSelector.DataSelectParams;
@ -125,16 +126,13 @@ public class TethysParameterPacker {
e1.printStackTrace();
}
Element elf = docf.getDocumentElement();
elList.add(elf);/**
elList.add(elf);
/**
* Is there a data filter ? If so, write it's
* XML parameters out here.
*/
Element pEl = xmlWriter.writeObjectData(docf, elf, filterParams, null);
}
// if (pEl != null) {
//// filterEl.appendChild(pEl);
// elf.appendChild(filterEl);
// }
}
}
if (paramOption == TethysExportParams.DETECTOR_DATASELECTOR) {
@ -177,6 +175,27 @@ public class TethysParameterPacker {
return elList;
}
public Element packObject(Object data) {
return packObject(data, "parameters");
}
public Element packObject(Object data, String elementName) {
Document doc = null;
QName qname = new QName(MarshalXML.schema, elementName, "ty");
JAXBElement<String> jaxel = new JAXBElement<String>(
qname, String.class, data.getClass().getCanonicalName());
try {
doc = marshaller.marshalToDOM(jaxel);
} catch (JAXBException | ParserConfigurationException e1) {
e1.printStackTrace();
}
Element el = doc.getDocumentElement();
Element pEl = xmlWriter.writeObjectData(doc, el, data, null);
return pEl;
}
/**
* Get a list of parent modules of the datablock, including it's own.
* @param dataBlock
@ -199,7 +218,7 @@ public class TethysParameterPacker {
return chain;
}
private boolean createElement(Document document, Element parentEl, Object paramData, PamParameterData pamParam, ArrayList<Object> objectHierarchy) {
private Element createElement(Document document, Element parentEl, Object paramData, PamParameterData pamParam, ArrayList<Object> objectHierarchy) {
Class<? extends Object> javaClass = paramData.getClass();
if (PamguardXMLWriter.isWritableType(javaClass)) {
String name = pamParam.getFieldName();
@ -209,63 +228,20 @@ public class TethysParameterPacker {
el.setTextContent(value);
parentEl.appendChild(el);
// QName qname = new QName(MarshalXML.schema, name, "ty");
// JAXBElement<String> jaxel = new JAXBElement<String>(
// qname, String.class, value);
//
//
// try {
// jxbm.marshal(jaxel, dom);
// } catch (JAXBException e) {
// e.printStackTrace();
// }
// Document doc = null;
// try {
// doc = marshaller.marshalToDOM(jaxel);
// } catch (JAXBException e) {
// e.printStackTrace();
// } catch (ParserConfigurationException e) {
// e.printStackTrace();
// }
// Element el = doc.getDocumentElement();
// return el;
return true;
return el;
}
if (javaClass.isArray()) {
return writeArray(document, parentEl, paramData, pamParam, objectHierarchy);
}
/*
*
if (javaClass.isArray()) {
return writeArray(doc, el, data, pamParam, objectHierarchy);
}
if (List.class.isAssignableFrom(javaClass)){
return writeList(doc, el, data, pamParam, objectHierarchy);
}
if (Map.class.isAssignableFrom(javaClass)){
return writeMap(doc, el, data, pamParam, objectHierarchy);
}
if (File.class.isAssignableFrom(javaClass)) {
return writeFile(doc, el, data, pamParam);
}
else {
Element e = makeElement(doc, pamParam.getFieldName(), data.getClass().getName());
el.appendChild(e);
writeObjectData(doc, e, data, objectHierarchy);
return e;
}
*/
return false;
return null;
}
private boolean writeArray(Document document, Element parentEl, Object paramData, PamParameterData pamParam,
private Element writeArray(Document document, Element parentEl, Object paramData, PamParameterData pamParam,
ArrayList<Object> objectHierarchy) {
if (paramData.getClass().isArray() == false) {
return false;
return null;
}
String name = pamParam.getFieldName();
Element el = document.createElement(name);
@ -274,10 +250,10 @@ public class TethysParameterPacker {
boolean ok = true;
for (int i = 0; i < n; i++) {
Object arrayEl = Array.get(paramData, i);
ok &= createElement(document, el, arrayEl, pamParam, objectHierarchy);
ok &= (createElement(document, el, arrayEl, pamParam, objectHierarchy) != null);
}
// TODO Auto-generated method stub
return ok;
return el;
}

View File

@ -1,5 +1,7 @@
package tethys.swing;
import static org.mockito.Mockito.RETURNS_DEEP_STUBS;
import java.awt.BorderLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
@ -105,25 +107,56 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa
// if (rowIndex < 0) {
// return null;
// }
NilusDataWrapper doc = detectionsForRow(rowIndex);
int colIndex = table.columnAtPoint(p);
switch (colIndex) {
case 0:
return "Tethys Detections document name";
if (doc != null) {
return "Tethys Detections document Id: " + doc.getDocumentId();
}
return "Tethys Detections document Id";
case 1:
if (dataBlock != null) {
return "Name of PAMGuard data stream: " + dataBlock.getLongDataName();
}
return "Name of PAMGuard data stream";
case 2:
return "Effort period";
return "Deployment type";
case 3:
return "Output granularity";
return "Output type and granularity ";
case 4:
return "Number of detection elements in document";
return "Effort period";
case 5:
return "Number of detection or localization elements in document";
case 6:
DescriptionType desc = findDescription(rowIndex);
if (desc != null) {
String str = "Document abstract: " + desc.getAbstract();
return str;
}
else {
return "Document abstract";
}
}
return "No tip";
}
private DescriptionType findDescription(int rowIndex) {
NilusDataWrapper doc = detectionsForRow(rowIndex);
if (doc == null) {
return null;
}
Object desc = doc.getGotObject("getDescription");
if (desc instanceof DescriptionType) {
return (DescriptionType) desc;
}
else {
return null;
}
}
@Override
public JComponent getComponent() {
return mainPanel;
@ -362,7 +395,7 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa
private class TableModel extends AbstractTableModel {
private String[] colNames = {"Document", "Detector", "Deployment", "Type", "Effort", "Granularity", "Count", "Abstract"};
private String[] colNames = {"Document", "Detector", "Deployment", "Type (Granularity)", "Effort", "Count", "Abstract"};
@Override
public int getRowCount() {
@ -413,7 +446,10 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa
return dataSource.getDeploymentId();
}
case 3:
return pDets.getCollection();
// String col = pDets.getCollection().collectionName();
//
// return pDets.getCollection();
return getType(pDets);
case 4:
// XMLGregorianCalendar start = dets.getEffort().getStart();
// XMLGregorianCalendar stop = dets.getEffort().getEnd();
@ -421,27 +457,8 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa
XMLGregorianCalendar stop = pDets.getEffortEnd();
return start + " to " + stop;
case 5:
Object effort = pDets.getGotObjects("getEffort");
if (effort instanceof DetectionEffort) {
DetectionEffort detectionEffort = (DetectionEffort) effort;
List<DetectionEffortKind> kinds = detectionEffort.getKind();
if (kinds == null) {
return null;
}
for (DetectionEffortKind kind : kinds) {
if (kind.getGranularity() != null) {
GranularityType granularity = kind.getGranularity();
return PDeployment.formatGranularity(granularity);
// if (granularity != null) {
// return granularity.getValue();
// }
}
}
}
break;
case 6:
return pDets.count;
case 7:
case 6:
DescriptionType desc = pDets.getDescription();
if (desc != null) {
return desc.getAbstract();
@ -451,4 +468,31 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa
}
}
private String getType(NilusDataWrapper<PDetections> pDets) {
String type;
if (pDets == null || pDets.nilusObject == null) {
return null;
}
type = pDets.getCollection().collectionName();
// if it's a detection, also get the granularity.
Object effort = pDets.getGotObjects("getEffort");
if (effort instanceof DetectionEffort) {
DetectionEffort detectionEffort = (DetectionEffort) effort;
List<DetectionEffortKind> kinds = detectionEffort.getKind();
if (kinds == null) {
return type;
}
for (DetectionEffortKind kind : kinds) {
if (kind.getGranularity() != null) {
GranularityType granularity = kind.getGranularity();
if (granularity != null) {
type += " (" + granularity.getValue() + ")";
}
}
}
}
return type;
}
}

View File

@ -299,7 +299,7 @@ public class DatablockSynchPanel extends TethysExportPanel {
long stop = synchInfo.getDataBlock().getPrimaryDataMap().getLastDataTime();
return String.format("%s - %s", PamCalendar.formatDBDateTime(start), PamCalendar.formatDBDateTime(stop));
case 3:
return synchInfo.getDetectionDocumentCount();
return synchInfo.getDetectionDocumentCount() + synchInfo.getLocalizationDocumentCount();
}
return null;
}

View File

@ -2,8 +2,12 @@ package tethys.swing;
import java.awt.BorderLayout;
import java.awt.Window;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.JMenuItem;
import javax.swing.JPanel;
import javax.swing.JPopupMenu;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
@ -26,11 +30,34 @@ public class XMLStringView extends PamDialog {
textArea.setEditable(false);
textArea.setCaretPosition(0);
JPopupMenu popMenu = new JPopupMenu();
textArea.setComponentPopupMenu(popMenu);
JMenuItem copyItem = new JMenuItem("Copy");
copyItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
textArea.copy();
}
});
JMenuItem selItem = new JMenuItem("Select All");
selItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
textArea.selectAll();
}
});
popMenu.add(copyItem);
popMenu.addSeparator();
popMenu.add(selItem);
getCancelButton().setVisible(false);
setModal(false);
getOkButton().setText("Close");
getOkButton().setToolTipText("Close window");
setModal(false);
}
public static void showDialog(Window parent, String collection, String documentId, String xmlString) {

View File

@ -101,12 +101,23 @@ public class DescriptionTypePanel {
tMethod.setText(null);
}
else {
checkDescription(description);
tObjectives.setText(description.getObjectives());
tAbstract.setText(description.getAbstract());
tMethod.setText(description.getMethod());
}
}
/**
* Auto fill some of the description fields.
* @param description
*/
private void checkDescription(DescriptionType description) {
if (description.getMethod() == null || description.getMethod().length() == 0) {
}
}
public boolean getParams(DescriptionType description) {
Window f = PamGui.findComponentWindow(mainPanel);
if (checkField(requireObjective, tObjectives) == false) {

View File

@ -81,6 +81,7 @@ public class DetectionsExportWizard extends PamWizard {
granularityCard.setParams(streamExportParams);
}
if (wizardCard == descriptionCard) {
streamExportParams.checkDescription();
descriptionCard.setParams(streamExportParams.getNilusDetectionDescription());
}
if (wizardCard == algorithmCard) {

View File

@ -10,6 +10,8 @@ import javax.swing.JMenuItem;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import Localiser.LocalisationAlgorithm;
import Localiser.LocalisationAlgorithmInfo;
import dataPlots.data.TDDataProviderRegister;
import dataPlotsFX.data.TDDataProviderRegisterFX;
import dataPlotsFX.whistlePlotFX.WhistleMoanProviderFX;
@ -30,7 +32,7 @@ import PamController.PamSettings;
import PamView.PamSidePanel;
import PamView.WrapperControlledGUISwing;
public class WhistleMoanControl extends PamControlledUnit implements PamSettings {
public class WhistleMoanControl extends PamControlledUnit implements PamSettings, LocalisationAlgorithm {
private WhistleToneConnectProcess whistleToneProcess;
@ -229,4 +231,9 @@ public class WhistleMoanControl extends PamControlledUnit implements PamSettings
public String getModuleSummary(boolean clear) {
return whistleToneProcess.getModuleSummary(clear);
}
@Override
public LocalisationAlgorithmInfo getAlgorithmInfo() {
return whistleToneProcess.getLocAlgorithmInfo();
}
}

View File

@ -15,6 +15,7 @@ import fftManager.FFTDataUnit;
import generalDatabase.PamDetectionLogging;
import generalDatabase.SQLLogging;
import Array.ArrayManager;
import Localiser.LocalisationAlgorithmInfo;
import Localiser.algorithms.Correlations;
import Localiser.algorithms.timeDelayLocalisers.bearingLoc.BearingLocaliser;
import Localiser.algorithms.timeDelayLocalisers.bearingLoc.BearingLocaliserSelector;
@ -1088,4 +1089,23 @@ public class WhistleToneConnectProcess extends PamProcess {
return sumText;
}
/**
* Get info on current localisation algorithm. Grab the BL from the
* first group that has one.
* @return
*/
public LocalisationAlgorithmInfo getLocAlgorithmInfo() {
if (shapeConnectors == null) {
return null;
}
for (int i = 0; i < shapeConnectors.length; i++) {
BearingLocaliser bl = shapeConnectors[i].bearingLocaliser;
if (bl != null && bl.getAlgorithmInfo() != null) {
return bl.getAlgorithmInfo();
}
}
return null;
}
}