Detections output

more work on detections output.
This commit is contained in:
Douglas Gillespie 2023-09-15 15:15:28 +01:00
parent db1cc75bc1
commit 158eedce8c
19 changed files with 486 additions and 137 deletions

View File

@ -0,0 +1,29 @@
package PamguardMVC;
/**
* @author dg50
* Levels of automation for the various datas in PAMGuard.
* Should be used within DataAutomationInfo to perhaps combine with other info in the future.
*
*/
public enum DataAutomation {
AUTOMATIC, MANUAL, MANUALANDAUTOMATIC;
@Override
public String toString() {
switch (this) {
case AUTOMATIC:
return "Automatic";
case MANUAL:
return "Manual";
case MANUALANDAUTOMATIC:
return "Manual and automatic";
default:
break;
}
return null;
}
}

View File

@ -0,0 +1,44 @@
package PamguardMVC;
/**
* Returned by datablocks, though default is null, to give information on how
* automatic the process was.
* @author dg50
*
*/
public class DataAutomationInfo {
private DataAutomation automation;
/**
* @param automation
*/
public DataAutomationInfo(DataAutomation automation) {
this.setAutomation(automation);
}
/**
* @return the automation
*/
public DataAutomation getAutomation() {
return automation;
}
/**
* @param automation the automation to set
*/
public void setAutomation(DataAutomation automation) {
this.automation = automation;
}
@Override
public String toString() {
if (automation == null) {
return "Unknown data automation";
}
return automation.toString();
}
}

View File

@ -3100,11 +3100,12 @@ public class PamDataBlock<Tunit extends PamDataUnit> extends PamObservable {
} }
/** /**
* Set a data provider for Tethys. * Get the level of automation employed by the generation of these data.
* @param tethysDataProvider the tethysDataProvider to set * Should ideally be completed for everything providing data to Tethys.
* @return level of automation for this data block.
*/ */
public void setTethysDataProvider(TethysDataProvider tethysDataProvider) { public DataAutomationInfo getDataAutomationInfo() {
this.tethysDataProvider = tethysDataProvider; return null;
} }
/** /**

View File

@ -2,6 +2,8 @@ package RightWhaleEdgeDetector;
import PamView.GroupedDataSource; import PamView.GroupedDataSource;
import PamView.GroupedSourceParameters; import PamView.GroupedSourceParameters;
import PamguardMVC.DataAutomation;
import PamguardMVC.DataAutomationInfo;
import PamguardMVC.PamProcess; import PamguardMVC.PamProcess;
import PamguardMVC.dataOffline.OfflineDataLoadInfo; import PamguardMVC.dataOffline.OfflineDataLoadInfo;
import PamguardMVC.dataSelector.DataSelectorCreator; import PamguardMVC.dataSelector.DataSelectorCreator;
@ -77,4 +79,9 @@ public class RWEDataBlock extends AbstractWhistleDataBlock<RWEDataUnit> implemen
return rwTethysDataProvider; return rwTethysDataProvider;
} }
@Override
public DataAutomationInfo getDataAutomationInfo() {
return new DataAutomationInfo(DataAutomation.AUTOMATIC);
}
} }

View File

@ -846,6 +846,11 @@ public class DBXMLQueries {
description.setMethod(getElementData(result, "Description.Method")); description.setMethod(getElementData(result, "Description.Method"));
description.setObjectives(getElementData(result, "Description.Objectives")); description.setObjectives(getElementData(result, "Description.Objectives"));
// get the effort start an end
String effStart = getElementData(result, "Effort.Start");
String effEnd = getElementData(result, "Effort.End");
detections.getEffort().setStart(TethysTimeFuncs.fromGregorianXML(effStart));
detections.getEffort().setEnd(TethysTimeFuncs.fromGregorianXML(effEnd));
// try to find the granularity. // try to find the granularity.
String granularityString = getElementData(result, "Effort.Kind.Granularity"); String granularityString = getElementData(result, "Effort.Kind.Granularity");
GranularityEnumType granularity = null; GranularityEnumType granularity = null;

View File

@ -4,6 +4,8 @@ import java.math.BigInteger;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.Iterator;
import java.util.Map.Entry;
import java.util.Set; import java.util.Set;
import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataBlock;
@ -29,10 +31,8 @@ import tethys.species.SpeciesMapItem;
*/ */
public class BinnedGranularityHandler extends GranularityHandler { public class BinnedGranularityHandler extends GranularityHandler {
private double binDurationSeconds; private long binDurationMillis;
private long binStartMillis, binEndMillis;
private TethysDataProvider dataProvider; private TethysDataProvider dataProvider;
private DataBlockSpeciesManager speciesManager; private DataBlockSpeciesManager speciesManager;
@ -43,7 +43,7 @@ public class BinnedGranularityHandler extends GranularityHandler {
TethysExportParams tethysExportParams, StreamExportParams streamExportParams) { TethysExportParams tethysExportParams, StreamExportParams streamExportParams) {
super(tethysControl, dataBlock, tethysExportParams, streamExportParams); super(tethysControl, dataBlock, tethysExportParams, streamExportParams);
binDurationSeconds = streamExportParams.binDurationS; binDurationMillis = (long) (streamExportParams.binDurationS*1000.);
dataProvider = dataBlock.getTethysDataProvider(tethysControl); dataProvider = dataBlock.getTethysDataProvider(tethysControl);
speciesManager = dataBlock.getDatablockSpeciesManager(); speciesManager = dataBlock.getDatablockSpeciesManager();
@ -52,61 +52,74 @@ public class BinnedGranularityHandler extends GranularityHandler {
@Override @Override
public void prepare(long timeMillis) { public void prepare(long timeMillis) {
long binStart = DetectionsHandler.roundDownBinStart(timeMillis, (long) (binDurationSeconds*1000)); // long binStart = DetectionsHandler.roundDownBinStart(timeMillis, binDurationMillis);
startBin(binStart); // startBin(binStart);
} }
private void startBin(long timeMillis) { // private void startBin(long timeMillis) {
binStartMillis = timeMillis; // binStartMillis = timeMillis;
binEndMillis = binStartMillis + (long) (binDurationSeconds*1000.); // binEndMillis = binStartMillis + binDurationMillis;
/* // /*
* now make a Detection object for every possible species that // * now make a Detection object for every possible species that
* this might throw out. // * this might throw out.
*/ // */
ArrayList<String> speciesCodes = speciesManager.getAllSpeciesCodes(); // ArrayList<String> speciesCodes = speciesManager.getAllSpeciesCodes();
String defaultCode = speciesManager.getDefaultSpeciesCode(); // String defaultCode = speciesManager.getDefaultSpeciesCode();
Detection det; // Detection det;
currentDetections.put(defaultCode, det = new Detection()); // currentDetections.put(defaultCode, det = new Detection());
det.setStart(TethysTimeFuncs.xmlGregCalFromMillis(binStartMillis)); // det.setStart(TethysTimeFuncs.xmlGregCalFromMillis(binStartMillis));
det.setEnd(TethysTimeFuncs.xmlGregCalFromMillis(binEndMillis)); // det.setEnd(TethysTimeFuncs.xmlGregCalFromMillis(binEndMillis));
det.setCount(BigInteger.ZERO); // det.setCount(BigInteger.ZERO);
det.setChannel(BigInteger.ZERO); // det.setChannel(BigInteger.ZERO);
// add codes at end, just before output. // // add codes at end, just before output.
if (speciesCodes != null) { // if (speciesCodes != null) {
for (String code : speciesCodes) { // for (String code : speciesCodes) {
currentDetections.put(code, det = new Detection()); // currentDetections.put(code, det = new Detection());
det.setStart(TethysTimeFuncs.xmlGregCalFromMillis(binStartMillis)); // det.setStart(TethysTimeFuncs.xmlGregCalFromMillis(binStartMillis));
det.setEnd(TethysTimeFuncs.xmlGregCalFromMillis(binEndMillis)); // det.setEnd(TethysTimeFuncs.xmlGregCalFromMillis(binEndMillis));
det.setCount(BigInteger.ZERO); // det.setCount(BigInteger.ZERO);
det.setChannel(BigInteger.ZERO); // det.setChannel(BigInteger.ZERO);
} // }
} // }
} // }
@Override @Override
public Detection[] addDataUnit(PamDataUnit dataUnit) { public Detection[] addDataUnit(PamDataUnit dataUnit) {
Detection[] detections = null; Detection[] completeDetections = closeBins(dataUnit.getTimeMilliseconds());
if (dataUnit.getTimeMilliseconds() >= binEndMillis) { // now look for new ones. First get the species of the dataUnit and find it in the hashmap
detections = closeBins(dataUnit.getTimeMilliseconds()); String groupName = getCallGroupName(dataUnit);
prepare(dataUnit.getTimeMilliseconds()); Detection det = currentDetections.get(groupName);
if (det == null) {
// need to make a new one.
det = new Detection();
long binStart = DetectionsHandler.roundDownBinStart(dataUnit.getTimeMilliseconds(), binDurationMillis);
det.setStart(TethysTimeFuncs.xmlGregCalFromMillis(binStart));
det.setEnd(TethysTimeFuncs.xmlGregCalFromMillis(binStart + binDurationMillis));
det.setCount(BigInteger.ONE);
det.setChannel(BigInteger.valueOf(dataUnit.getChannelBitmap()));
// this should always return something, so am going to crash if it doesn't.
// may revisit this later on if we've unassigned things we don't want to label
// in which case they should be rejected earlier than this.
SpeciesMapItem speciesStuff = speciesManager.getSpeciesItem(dataUnit);
SpeciesIDType species = new SpeciesIDType();
species.setValue(BigInteger.valueOf(speciesStuff.getItisCode()));
det.setSpeciesId(species);
if (speciesStuff.getCallType() != null) {
det.getCall().add(speciesStuff.getCallType());
}
currentDetections.put(groupName, det);
} }
String speciesCode = speciesManager.getSpeciesCode(dataUnit); else {
Detection det = currentDetections.get(speciesCode); // add to current detection. Set new end time and increment count
if (det != null) { int count = det.getCount().intValue() + 1;
/*
* Increase the detection count
*/
int count = det.getCount().intValue();
count++;
det.setCount(BigInteger.valueOf(count)); det.setCount(BigInteger.valueOf(count));
/* int chan = det.getChannel().intValue();
* Add to the channel map too ... chan |= dataUnit.getChannelBitmap();
*/ det.setChannel(BigInteger.valueOf(chan));
int channel = det.getChannel().intValue();
channel |= dataUnit.getChannelBitmap();
det.setChannel(BigInteger.valueOf(channel));
} }
return detections;
return completeDetections;
} }
/** /**
@ -115,33 +128,32 @@ public class BinnedGranularityHandler extends GranularityHandler {
* @param timeMilliseconds * @param timeMilliseconds
* @return * @return
*/ */
private Detection[] closeBins(long timeMilliseconds) { private synchronized Detection[] closeBins(long timeMilliseconds) {
Set<String> speciesKeys = currentDetections.keySet(); Set<String> speciesKeys = currentDetections.keySet();
int n = speciesKeys.size(); int n = speciesKeys.size();
int nGood = 0; int nGood = 0;
DataBlockSpeciesMap speciesMap = speciesManager.getDatablockSpeciesMap(); DataBlockSpeciesMap speciesMap = speciesManager.getDatablockSpeciesMap();
Detection detections[] = new Detection[n]; Detection detections[] = new Detection[n];
for (String key : speciesKeys) { Iterator<Entry<String, Detection>> iter = currentDetections.entrySet().iterator();
Detection det = currentDetections.get(key); while (iter.hasNext()) {
Entry<String, Detection> entry = iter.next();
Detection det = entry.getValue();
long detEnd = TethysTimeFuncs.millisFromGregorianXML(det.getEnd());
if (timeMilliseconds < detEnd) {
// we're not at the end of the bin, so carry on.
continue;
}
// we've reached the end of the bin, so remove it from the map
iter.remove();
// now decide if we want to keep it or not.
int callCount = det.getCount().intValue(); int callCount = det.getCount().intValue();
if (callCount < Math.max(streamExportParams.minBinCount,1)) { if (callCount < Math.max(streamExportParams.minBinCount,1)) {
continue; continue; // won't add to output list
}
SpeciesMapItem speciesStuff = speciesMap.getItem(key); // should be non null!
if (speciesStuff == null) {
continue;
}
SpeciesIDType species = new SpeciesIDType();
species.setValue(BigInteger.valueOf(speciesStuff.getItisCode()));
det.setSpeciesId(species);
if (speciesStuff.getCallType() != null) {
det.getCall().add(speciesStuff.getCallType());
} }
detections[nGood++] = det; detections[nGood++] = det;
} }
/* /*
* Clean up the end of the array and return detections that have enough calls. * Clean up the end of the array and return detections that have enough calls.
*/ */

View File

@ -349,9 +349,9 @@ public class DetectionsHandler {
/** /**
* Round a bin start so that it's aligned correctly with * Round a bin start so that it's aligned correctly with
* day starts. * day starts.
* @param binStart * @param binStart in milliseconds
* @param binInterval * @param binInterval in milliseconds
* @return * @return rounded time.
*/ */
public static long roundDownBinStart(long binStart, long binInterval) { public static long roundDownBinStart(long binStart, long binInterval) {
binStart/=binInterval; binStart/=binInterval;
@ -441,6 +441,10 @@ public class DetectionsHandler {
// currentDetections = null; // currentDetections = null;
// } // }
} }
Detection dets[] = granularityHandler.cleanup(deployment.getAudioEnd());
if (dets != null) {
exportCount += dets.length;
}
@ -578,8 +582,9 @@ public class DetectionsHandler {
e.printStackTrace(); e.printStackTrace();
return null; return null;
} }
TethysDataProvider dataProvider = dataBlock.getTethysDataProvider(tethysControl);
String prefix = deployment.deployment.getId(); String prefix = deployment.deployment.getId() + "_" + dataProvider.getDetectionsName();
String fullId = ""; String fullId = "";
/* /*
* Check the document name isn't already used and increment id as necessary. * Check the document name isn't already used and increment id as necessary.
@ -599,7 +604,6 @@ public class DetectionsHandler {
detections.setDataSource(dataSource); detections.setDataSource(dataSource);
AlgorithmType algorithm = detections.getAlgorithm(); AlgorithmType algorithm = detections.getAlgorithm();
TethysDataProvider dataProvider = dataBlock.getTethysDataProvider(tethysControl);
if (dataProvider != null) { if (dataProvider != null) {
algorithm = dataProvider.getAlgorithm(); algorithm = dataProvider.getAlgorithm();
// detections.setAlgorithm(algorithm); // detections.setAlgorithm(algorithm);

View File

@ -6,6 +6,7 @@ import java.util.HashMap;
import java.util.Iterator; import java.util.Iterator;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Set; import java.util.Set;
import java.util.Map.Entry;
import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataBlock;
import PamguardMVC.PamDataUnit; import PamguardMVC.PamDataUnit;
@ -54,8 +55,8 @@ public class EncounterGranularityHandler extends GranularityHandler {
public Detection[] addDataUnit(PamDataUnit dataUnit) { public Detection[] addDataUnit(PamDataUnit dataUnit) {
Detection[] completeDetections = checkCurrentEncounters(dataUnit.getTimeMilliseconds()); Detection[] completeDetections = checkCurrentEncounters(dataUnit.getTimeMilliseconds());
// now look for new ones. First get the species of the dataUnit and find it in the hashmap // now look for new ones. First get the species of the dataUnit and find it in the hashmap
String speciesCode = speciesManager.getSpeciesCode(dataUnit); String groupName = getCallGroupName(dataUnit);
Detection det = currentDetections.get(speciesCode); Detection det = currentDetections.get(groupName);
if (det == null) { if (det == null) {
// need to make a new one. // need to make a new one.
det = new Detection(); det = new Detection();
@ -73,7 +74,7 @@ public class EncounterGranularityHandler extends GranularityHandler {
if (speciesStuff.getCallType() != null) { if (speciesStuff.getCallType() != null) {
det.getCall().add(speciesStuff.getCallType()); det.getCall().add(speciesStuff.getCallType());
} }
currentDetections.put(speciesCode, det); currentDetections.put(groupName, det);
} }
else { else {
// add to current detection. Set new end time and increment count // add to current detection. Set new end time and increment count
@ -98,16 +99,18 @@ public class EncounterGranularityHandler extends GranularityHandler {
Set<String> keys = currentDetections.keySet(); Set<String> keys = currentDetections.keySet();
int nGood = 0; int nGood = 0;
Detection[] newDetections = new Detection[currentDetections.size()]; Detection[] newDetections = new Detection[currentDetections.size()];
for (String aKey : keys) { Iterator<Entry<String, Detection>> iter = currentDetections.entrySet().iterator();
Detection aDet = currentDetections.get(aKey); while (iter.hasNext()) {
Long detEnd = TethysTimeFuncs.millisFromGregorianXML(aDet.getEnd()); Entry<String, Detection> entry = iter.next();
Detection aDet = entry.getValue();
long detEnd = TethysTimeFuncs.millisFromGregorianXML(aDet.getEnd());
if (timeMilliseconds-detEnd > maxGapMillis) { if (timeMilliseconds-detEnd > maxGapMillis) {
// only keep if it's got a min number of calls. // only keep if it's got a min number of calls.
if (aDet.getCount().intValue() >= streamExportParams.minBinCount) { if (aDet.getCount().intValue() >= streamExportParams.minBinCount) {
newDetections[nGood++] = aDet; newDetections[nGood++] = aDet;
} }
// remove from set. A new one will be created only when required. // remove from set. A new one will be created only when required.
currentDetections.remove(aKey); iter.remove();
} }
} }

View File

@ -7,6 +7,7 @@ import nilus.GranularityEnumType;
import tethys.TethysControl; import tethys.TethysControl;
import tethys.output.StreamExportParams; import tethys.output.StreamExportParams;
import tethys.output.TethysExportParams; import tethys.output.TethysExportParams;
import tethys.species.DataBlockSpeciesManager;
public abstract class GranularityHandler { public abstract class GranularityHandler {
@ -18,6 +19,8 @@ public abstract class GranularityHandler {
protected StreamExportParams streamExportParams; protected StreamExportParams streamExportParams;
private DataBlockSpeciesManager speciesManager;
/** /**
* @param tethysControl * @param tethysControl
* @param dataBlock * @param dataBlock
@ -30,6 +33,7 @@ public abstract class GranularityHandler {
this.dataBlock = dataBlock; this.dataBlock = dataBlock;
this.tethysExportParams = tethysExportParams; this.tethysExportParams = tethysExportParams;
this.streamExportParams = streamExportParams; this.streamExportParams = streamExportParams;
speciesManager = dataBlock.getDatablockSpeciesManager();
} }
/** /**
@ -48,6 +52,23 @@ public abstract class GranularityHandler {
*/ */
public abstract Detection[] addDataUnit(PamDataUnit dataUnit); public abstract Detection[] addDataUnit(PamDataUnit dataUnit);
/**
* Get a grouping name for the call. This may just be the calls species code,
* or it may be appended with the channel number. This is used to find bin and
* encounter data in HashMaps in
* @param dataUnit
* @return
*/
public String getCallGroupName(PamDataUnit dataUnit) {
String groupName = speciesManager.getSpeciesCode(dataUnit);
if (groupName == null) {
groupName = "NullSpecies";
}
if (streamExportParams.separateChannels) {
groupName += String.format("Chan%d", dataUnit.getChannelBitmap());
}
return groupName;
}
/** /**
* Called after end end of all data units to get the last bin / encounter. <p> * Called after end end of all data units to get the last bin / encounter. <p>
* *

View File

@ -3,15 +3,20 @@ package tethys.output;
import java.io.IOException; import java.io.IOException;
import java.io.Serializable; import java.io.Serializable;
import PamController.PamController;
import PamguardMVC.PamDataBlock;
import nilus.DescriptionType; import nilus.DescriptionType;
import nilus.GranularityEnumType; import nilus.GranularityEnumType;
import tethys.TethysControl;
import tethys.niluswraps.PDescriptionType; import tethys.niluswraps.PDescriptionType;
import tethys.pamdata.TethysDataProvider;
/** /**
* Parameters controlling export of a single stream. * Parameters controlling export of a single stream.
* Starts just with a boolean 'selected', but may grow. * Starts just with a boolean 'selected', but may grow.
* These all contain data names rather than references to a Datablock so that * These all contain data names rather than references to a Datablock so that
* they can be serialised. * they can be serialised. However, created with TethysControl and datablock
* so that some stuff canbe automatically initialised.
* @author dg50 * @author dg50
* *
*/ */
@ -19,24 +24,44 @@ public class StreamExportParams implements Serializable {
public static final long serialVersionUID = 1L; public static final long serialVersionUID = 1L;
public StreamExportParams(String longDataName, boolean selected) { /**
super(); * Datablock long data name (used instead of datablock
this.longDataName = longDataName; * reference so this object and serialise.
this.selected = selected; */
}
public String longDataName; public String longDataName;
public boolean selected; public boolean selected;
/**
* Granularity type, binned, call, encounter, grouped.
*/
public GranularityEnumType granularity = GranularityEnumType.CALL; public GranularityEnumType granularity = GranularityEnumType.CALL;
/**
* Bin duration, seconds.
*/
public double binDurationS = 60; public double binDurationS = 60;
/**
* Minimum encounter gap, seconds
*/
public double encounterGapS = 60; public double encounterGapS = 60;
/**
* Minimum count for a bin to be retained.
*/
public int minBinCount = 1; public int minBinCount = 1;
/**
* Minimum count for an encounter to be retained.
*/
public int minEncounterCount = 1;
/**
* Keep channels separate when using binned data.
*/
public boolean separateChannels = true;
/* /*
* Can't have this here since it isn't serializable. * Can't have this here since it isn't serializable.
*/ */
@ -48,6 +73,25 @@ public class StreamExportParams implements Serializable {
} }
return detectionDescription; return detectionDescription;
} }
public StreamExportParams(TethysControl tethysControl, PamDataBlock dataBlock, boolean selected) {
super();
this.longDataName = dataBlock.getLongDataName();
this.selected = selected;
autoFill(tethysControl, dataBlock);
}
/**
* Try to put some information automatically into the Methods.
* @param dataBlock2
* @param tethysControl
*/
private void autoFill(TethysControl tethysControl, PamDataBlock dataBlock) {
// there should always be a data provider or we'd never have got this far.
TethysDataProvider dataProvider = dataBlock.getTethysDataProvider(tethysControl);
PDescriptionType desc = getDetectionDescription();
desc.setMethod(dataProvider.getDetectionsMethod());
}
/** /**
* Get the nilus detection description * Get the nilus detection description

View File

@ -151,7 +151,7 @@ public class TethysExportDialog extends PamDialog {
} }
int nSel = 0; int nSel = 0;
for (DataStreamSet streamSet : dataStreamSets) { for (DataStreamSet streamSet : dataStreamSets) {
StreamExportParams streamOpts = new StreamExportParams(streamSet.dataBlock.getLongDataName(), streamSet.checkBox.isSelected()); StreamExportParams streamOpts = new StreamExportParams(tethysControl, streamSet.dataBlock, streamSet.checkBox.isSelected());
exportParams.setStreamParams(streamSet.dataBlock, streamOpts); exportParams.setStreamParams(streamSet.dataBlock, streamOpts);
nSel++; nSel++;
} }

View File

@ -14,6 +14,7 @@ import PamController.PamSettings;
import PamController.PamguardVersionInfo; import PamController.PamguardVersionInfo;
import PamController.settings.output.xml.PamguardXMLWriter; import PamController.settings.output.xml.PamguardXMLWriter;
import PamUtils.XMLUtils; import PamUtils.XMLUtils;
import PamguardMVC.DataAutomationInfo;
import PamguardMVC.DataUnitBaseData; import PamguardMVC.DataUnitBaseData;
import PamguardMVC.PamDataBlock; import PamguardMVC.PamDataBlock;
import PamguardMVC.PamDataUnit; import PamguardMVC.PamDataUnit;
@ -30,6 +31,7 @@ import nilus.Detection;
import nilus.Detection.Parameters; import nilus.Detection.Parameters;
import nilus.Detection.Parameters.UserDefined; import nilus.Detection.Parameters.UserDefined;
import nilus.DetectionEffortKind; import nilus.DetectionEffortKind;
import nilus.GranularityEnumType;
import nilus.Helper; import nilus.Helper;
import nilus.SpeciesIDType; import nilus.SpeciesIDType;
import tethys.TethysControl; import tethys.TethysControl;
@ -90,11 +92,11 @@ public class AutoTethysProvider implements TethysDataProvider {
return schema; return schema;
} }
@Override // @Override
public TethysDataPoint getDataPoint(PamDataUnit pamDataUnit) { // public TethysDataPoint getDataPoint(PamDataUnit pamDataUnit) {
// TODO Auto-generated method stub // // TODO Auto-generated method stub
return null; // return null;
} // }
@Override @Override
public DescriptionType getDescription(Deployment deployment, TethysExportParams tethysExportParams) { public DescriptionType getDescription(Deployment deployment, TethysExportParams tethysExportParams) {
@ -488,4 +490,49 @@ public class AutoTethysProvider implements TethysDataProvider {
} }
@Override
public String getDetectionsMethod() {
/*
* could really do with knowing what type of detector we're dealing with, i.e. if it's
* automatic or manual. For most blocks this is fixed, though some may have a mixture of both !
*/
DataAutomationInfo dataAutomation = pamDataBlock.getDataAutomationInfo();
String method;
PamControlledUnit pcu = pamDataBlock.getParentProcess().getPamControlledUnit();
if (dataAutomation == null) {
method = String.format("Processing using the PAMGuard %s", pcu.getUnitType());
}
else {
method = String.format("%s processing using the PAMGuard %s", dataAutomation.getAutomation(), pcu.getUnitType());
}
return method;
}
@Override
public GranularityEnumType[] getAllowedGranularities() {
GranularityEnumType[] allowed = {GranularityEnumType.CALL, GranularityEnumType.BINNED, GranularityEnumType.ENCOUNTER};
return allowed;
}
@Override
public String getDetectionsName() {
PamProcess process = pamDataBlock.getParentProcess();
PamControlledUnit pcu = process.getPamControlledUnit();
String pcuName = pcu.getUnitName();
String blockName = pamDataBlock.getDataName();
String documentName;
/**
* If the datablock name is the same as the unit name, no need to repeat onesself.
*/
if (pcuName.equals(blockName)) {
documentName = new String(pcuName); // copy it, since we're about to modify it!
}
else {
documentName = pcuName + " " + blockName;
}
documentName = documentName.replace(' ', '_');
return documentName;
}
} }

View File

@ -9,6 +9,7 @@ import nilus.Deployment;
import nilus.DescriptionType; import nilus.DescriptionType;
import nilus.Detection; import nilus.Detection;
import nilus.DetectionEffortKind; import nilus.DetectionEffortKind;
import nilus.GranularityEnumType;
import tethys.niluswraps.PDeployment; import tethys.niluswraps.PDeployment;
import tethys.output.StreamExportParams; import tethys.output.StreamExportParams;
import tethys.output.TethysExportParams; import tethys.output.TethysExportParams;
@ -38,8 +39,14 @@ public interface TethysDataProvider {
* @param pamDataUnit * @param pamDataUnit
* @return * @return
*/ */
public TethysDataPoint getDataPoint(PamDataUnit pamDataUnit); // public TethysDataPoint getDataPoint(PamDataUnit pamDataUnit);
/**
* Get a standard Method string for each detector. This can be a bit
* verbose and might even have a reference to a paper ? Is this the best place for this ?
* @return
*/
public String getDetectionsMethod();
/** /**
* Get DescriptionType object to include in a Tethys Detections document. * Get DescriptionType object to include in a Tethys Detections document.
@ -55,6 +62,22 @@ public interface TethysDataProvider {
* @return Algorithm information * @return Algorithm information
*/ */
public AlgorithmType getAlgorithm(); public AlgorithmType getAlgorithm();
/**
* Get a list of allowed granularity types for this output
* @return list of granularities.
*/
public GranularityEnumType[] getAllowedGranularities();
/**
* Get a name for the detections documents. This will be appended
* to the Deployment name and may also have a number after it. <br>
* Note that the name isn't really important since all the matching between
* different documents is done internally, but it helps to make everything
* human readable.
* @return A name, similar to datablock.getLongDataName(), but no spaces.
*/
public String getDetectionsName();
/** /**

View File

@ -17,6 +17,7 @@ import javax.swing.JTable;
import javax.swing.border.TitledBorder; import javax.swing.border.TitledBorder;
import javax.swing.table.AbstractTableModel; import javax.swing.table.AbstractTableModel;
import javax.swing.table.JTableHeader; import javax.swing.table.JTableHeader;
import javax.xml.datatype.XMLGregorianCalendar;
import PamView.PamGui; import PamView.PamGui;
import PamView.dialog.warn.WarnOnce; import PamView.dialog.warn.WarnOnce;
@ -97,10 +98,12 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa
case 1: case 1:
return "Name of PAMGuard data stream"; return "Name of PAMGuard data stream";
case 2: case 2:
return "Output granularity"; return "Effort period";
case 3: case 3:
return "Number of detection elements in document"; return "Output granularity";
case 4: case 4:
return "Number of detection elements in document";
case 5:
return "Document abstract"; return "Document abstract";
} }
@ -229,7 +232,7 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa
private class TableModel extends AbstractTableModel { private class TableModel extends AbstractTableModel {
private String[] colNames = {"Document", "Detector", "Granularity", "Count", "Abstract"}; private String[] colNames = {"Document", "Detector", "Effort", "Granularity", "Count", "Abstract"};
@Override @Override
public int getRowCount() { public int getRowCount() {
@ -272,6 +275,10 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa
} }
return pDets.dataBlock.getDataName(); return pDets.dataBlock.getDataName();
case 2: case 2:
XMLGregorianCalendar start = dets.getEffort().getStart();
XMLGregorianCalendar stop = dets.getEffort().getEnd();
return start + " to " + stop;
case 3:
List<DetectionEffortKind> kinds = dets.getEffort().getKind(); List<DetectionEffortKind> kinds = dets.getEffort().getKind();
if (kinds == null) { if (kinds == null) {
return null; return null;
@ -286,9 +293,9 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa
} }
} }
break; break;
case 3:
return pDets.count;
case 4: case 4:
return pDets.count;
case 5:
return dets.getDescription().getAbstract(); return dets.getDescription().getAbstract();
} }
return null; return null;

View File

@ -156,7 +156,7 @@ public class DatablockSynchPanel extends TethysGUIPanel {
private class SynchTableModel extends AbstractTableModel { private class SynchTableModel extends AbstractTableModel {
String[] columnNames = {"Data Stream", "N PAM Datas", "PAMGuard Time", "Tethys Documents", "Tethys Time", "Options"}; String[] columnNames = {"Data Stream", "N PAM Datas", "PAMGuard Time", "Tethys Documents"};//, "Tethys Time", "Options"};
@Override @Override
public int getRowCount() { public int getRowCount() {

View File

@ -14,6 +14,7 @@ import javax.swing.JPanel;
import javax.swing.JPopupMenu; import javax.swing.JPopupMenu;
import javax.swing.JScrollPane; import javax.swing.JScrollPane;
import javax.swing.JTable; import javax.swing.JTable;
import javax.swing.ListSelectionModel;
import javax.swing.table.AbstractTableModel; import javax.swing.table.AbstractTableModel;
import PamController.PamController; import PamController.PamController;
@ -57,7 +58,9 @@ public class TethysDocumentTable implements PamDialogPanel {
mainPanel.add(BorderLayout.CENTER, scrollPane); mainPanel.add(BorderLayout.CENTER, scrollPane);
new SwingTableColumnWidths(tethysControl.getUnitName()+"TethysDocumentsTable", mainTable); new SwingTableColumnWidths(tethysControl.getUnitName()+"TethysDocumentsTable", mainTable);
this.setCollectionName(collectionName); this.setCollectionName(collectionName);
mainTable.addMouseListener(new TableMouse()); mainTable.addMouseListener(new TableMouse());
mainTable.setRowSelectionAllowed(true);
mainTable.setSelectionMode(ListSelectionModel.MULTIPLE_INTERVAL_SELECTION);
} }
public void updateTableData() { public void updateTableData() {
@ -94,6 +97,7 @@ public class TethysDocumentTable implements PamDialogPanel {
if (row < 0|| row >= documentNames.size()) { if (row < 0|| row >= documentNames.size()) {
return; return;
} }
String docName = documentNames.get(row); String docName = documentNames.get(row);
JPopupMenu popMenu = new JPopupMenu(); JPopupMenu popMenu = new JPopupMenu();
JMenuItem menuItem = new JMenuItem("Show document " + docName); JMenuItem menuItem = new JMenuItem("Show document " + docName);
@ -104,14 +108,31 @@ public class TethysDocumentTable implements PamDialogPanel {
} }
}); });
popMenu.add(menuItem); popMenu.add(menuItem);
menuItem = new JMenuItem("Delete document " + docName);
menuItem.addActionListener(new ActionListener() {
@Override int[] rows = mainTable.getSelectedRows();
public void actionPerformed(ActionEvent e) { if (rows != null && rows.length == 1) {
deleteDocument(docName); // docName = documentNames.get(rows[0]);
} menuItem = new JMenuItem("Delete document " + docName);
}); menuItem.addActionListener(new ActionListener() {
popMenu.add(menuItem); @Override
public void actionPerformed(ActionEvent e) {
deleteDocument(docName);
}
});
popMenu.add(menuItem);
}
else if (rows != null && rows.length > 1) {
String mt = String.format("Delete multiple (%d) documents", rows.length);
menuItem = new JMenuItem(mt);
menuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
deleteDocuments(rows);
}
});
popMenu.add(menuItem);
}
popMenu.show(e.getComponent(), e.getX(), e.getY()); popMenu.show(e.getComponent(), e.getX(), e.getY());
} }
@ -134,6 +155,32 @@ public class TethysDocumentTable implements PamDialogPanel {
updateTableData(); updateTableData();
} }
private void deleteDocuments(int[] rows) {
int ans = WarnOnce.showNamedWarning("deletedoc"+collectionName, PamController.getMainFrame(), "Delete documents",
"Are you sure you want to delete multiple documents ", WarnOnce.OK_CANCEL_OPTION);
if (ans != WarnOnce.OK_OPTION) {
return;
}
/*
* make a new list before anything is deleted since the
* man list will get updated during deletion and be out of date.
*/
String[] docNames = new String[rows.length];
for (int i = 0; i < rows.length; i++) {
docNames[i] = documentNames.get(rows[i]);
}
// now it's safe to delete them.
for (int i = 0; i < docNames.length; i++) {
try {
tethysControl.getDbxmlConnect().removeDocument(collectionName, docNames[i]);
} catch (TethysException e) {
System.out.println("Failed to delete " + docNames[i]);
System.out.println(e.getMessage());
}
}
updateTableData();
}
private class TableModel extends AbstractTableModel { private class TableModel extends AbstractTableModel {
private String[] columnNames = {"", "Document Id/Name"}; private String[] columnNames = {"", "Document Id/Name"};

View File

@ -39,6 +39,13 @@ public class DescriptionTypePanel {
private static final int ctrlWidth = 40; private static final int ctrlWidth = 40;
public static final String objectivesTip = "What are the objectives of this effort? Examples:\r\n"
+ "Beamform to increase SNR for detection.\r\n"
+ "Detect every click of a rare species.\r\n"
+ "Verify data quality.";
public static final String abstractTip = "Overview of effort.";
public static final String methodTip = "High-level description of the method used.";
public DescriptionTypePanel(String bordertitle, boolean requireObjective, boolean requireAbstract, boolean requireMethod) { public DescriptionTypePanel(String bordertitle, boolean requireObjective, boolean requireAbstract, boolean requireMethod) {
this.requireObjective = requireObjective; this.requireObjective = requireObjective;
this.requireAbstract = requireAbstract; this.requireAbstract = requireAbstract;
@ -51,6 +58,16 @@ public class DescriptionTypePanel {
tObjectives = new JTextArea(12, ctrlWidth); tObjectives = new JTextArea(12, ctrlWidth);
tAbstract = new JTextArea(8, ctrlWidth); tAbstract = new JTextArea(8, ctrlWidth);
tMethod = new JTextArea(9, ctrlWidth); tMethod = new JTextArea(9, ctrlWidth);
tObjectives.setLineWrap(true);
tObjectives.setWrapStyleWord(true);
tAbstract.setLineWrap(true);
tAbstract.setWrapStyleWord(true);
tMethod.setLineWrap(true);
tMethod.setWrapStyleWord(true);
tObjectives.setToolTipText(objectivesTip);
tAbstract.setToolTipText(abstractTip);
tMethod.setToolTipText(methodTip);
mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS)); mainPanel.setLayout(new BoxLayout(mainPanel, BoxLayout.Y_AXIS));
addScrollablePanel(tObjectives, "Objectives"); addScrollablePanel(tObjectives, "Objectives");
@ -78,6 +95,11 @@ public class DescriptionTypePanel {
tAbstract.setText(null); tAbstract.setText(null);
tMethod.setText(null); tMethod.setText(null);
} }
else {
tObjectives.setText(description.getObjectives());
tAbstract.setText(description.getAbstract());
tMethod.setText(description.getMethod());
}
} }
public boolean getParams(PDescriptionType description) { public boolean getParams(PDescriptionType description) {

View File

@ -39,7 +39,7 @@ public class DetectionsExportWizard extends PamDialog {
streamExportParams = tethysControl.getTethysExportParams().getStreamParams(dataBlock); streamExportParams = tethysControl.getTethysExportParams().getStreamParams(dataBlock);
if (streamExportParams == null) { if (streamExportParams == null) {
streamExportParams = new StreamExportParams(dataBlock.getLongDataName(), false); streamExportParams = new StreamExportParams(tethysControl, dataBlock, false);
} }
cardLayout = new CardLayout(); cardLayout = new CardLayout();

View File

@ -30,6 +30,7 @@ import nilus.GranularityEnumType;
import tethys.TethysControl; import tethys.TethysControl;
import tethys.niluswraps.PGranularityType; import tethys.niluswraps.PGranularityType;
import tethys.output.StreamExportParams; import tethys.output.StreamExportParams;
import tethys.pamdata.TethysDataProvider;
public class GranularityCard extends ExportWizardCard { public class GranularityCard extends ExportWizardCard {
@ -37,7 +38,9 @@ public class GranularityCard extends ExportWizardCard {
private JTextArea dataSelectionText; private JTextArea dataSelectionText;
private JTextField binLength, minCalls, encounterGap; private JTextField binLength, minBinnedCalls, encounterGap, minEncounterCalls;
private JRadioButton groupChannels, separateChannels;
private DataSelector dataSelector; private DataSelector dataSelector;
@ -45,51 +48,68 @@ public class GranularityCard extends ExportWizardCard {
private int encounterIndex, binnedIndex; private int encounterIndex, binnedIndex;
private GranularityEnumType[] allowedGranularities;
public GranularityCard(DetectionsExportWizard detectionsExportWizard, TethysControl tethysControl, PamDataBlock dataBlock) { public GranularityCard(DetectionsExportWizard detectionsExportWizard, TethysControl tethysControl, PamDataBlock dataBlock) {
super(tethysControl, "Granularity", dataBlock); super(tethysControl, "Granularity", dataBlock);
this.detectionsExportWizard = detectionsExportWizard; this.detectionsExportWizard = detectionsExportWizard;
setLayout(new BoxLayout(this, BoxLayout.Y_AXIS)); setLayout(new BoxLayout(this, BoxLayout.Y_AXIS));
TethysDataProvider tethysDataProvider = dataBlock.getTethysDataProvider(tethysControl);
// granularity // granularity
GranularityEnumType[] grans = GranularityEnumType.values(); allowedGranularities = tethysDataProvider.getAllowedGranularities();
granularities = new JRadioButton[grans.length]; granularities = new JRadioButton[allowedGranularities.length];
JPanel granPanel = new WestAlignedPanel(new GridBagLayout()); JPanel granPanel = new WestAlignedPanel(new GridBagLayout());
GridBagConstraints c = new PamGridBagContraints(); GridBagConstraints c = new PamGridBagContraints();
granPanel.setBorder(new TitledBorder("Granularity")); granPanel.setBorder(new TitledBorder("Granularity"));
ButtonGroup granGroup = new ButtonGroup(); ButtonGroup granGroup = new ButtonGroup();
GranularityChange gc = new GranularityChange(); GranularityChange gc = new GranularityChange();
for (int i = 0; i < grans.length; i++) { for (int i = 0; i < allowedGranularities.length; i++) {
c.gridx = 0; c.gridx = 0;
granularities[i] = new JRadioButton(PGranularityType.prettyString(grans[i])); granularities[i] = new JRadioButton(PGranularityType.prettyString(allowedGranularities[i]));
granularities[i].setToolTipText(PGranularityType.toolTip(grans[i])); granularities[i].setToolTipText(PGranularityType.toolTip(allowedGranularities[i]));
granularities[i].addActionListener(gc); granularities[i].addActionListener(gc);
granPanel.add(granularities[i], c); granPanel.add(granularities[i], c);
granGroup.add(granularities[i]); granGroup.add(granularities[i]);
if (grans[i] == GranularityEnumType.BINNED) { if (allowedGranularities[i] == GranularityEnumType.BINNED) {
binnedIndex = i; binnedIndex = i;
c.gridx++; c.gridx++;
granPanel.add(new JLabel(" bin duration ", JLabel.RIGHT), c); granPanel.add(new JLabel(" Bin duration ", JLabel.RIGHT), c);
c.gridx++; c.gridx++;
granPanel.add(binLength = new JTextField(5), c); granPanel.add(binLength = new JTextField(5), c);
c.gridx++; c.gridx++;
granPanel.add(new JLabel("(s), min Calls", JLabel.LEFT), c); granPanel.add(new JLabel("(s), Min Calls", JLabel.LEFT), c);
c.gridx++; c.gridx++;
granPanel.add(minCalls = new JTextField(5), c); granPanel.add(minBinnedCalls = new JTextField(5), c);
binLength.setToolTipText("Time bin duration in seconds"); binLength.setToolTipText("Time bin duration in seconds");
minCalls.setToolTipText("Minimum number of calls for a bin to be output"); minBinnedCalls.setToolTipText("Minimum number of calls for a bin to be output");
} }
if (grans[i] == GranularityEnumType.ENCOUNTER) { if (allowedGranularities[i] == GranularityEnumType.ENCOUNTER) {
encounterIndex = i; encounterIndex = i;
c.gridx++; c.gridx++;
granPanel.add(new JLabel(" min gap ", JLabel.RIGHT), c); granPanel.add(new JLabel(" Minimum gap ", JLabel.RIGHT), c);
c.gridx++; c.gridx++;
granPanel.add(encounterGap = new JTextField(5), c); granPanel.add(encounterGap = new JTextField(5), c);
c.gridx++; c.gridx++;
granPanel.add(new JLabel("(s) ", JLabel.LEFT), c); granPanel.add(new JLabel("(s), Min Calls", JLabel.LEFT), c);
c.gridx++;
granPanel.add(minEncounterCalls = new JTextField(5), c);
encounterGap.setToolTipText("Minimum gap between separate encounters"); encounterGap.setToolTipText("Minimum gap between separate encounters");
minEncounterCalls.setToolTipText("Minimum number of calls for an encounter to be output");
} }
c.gridy++; c.gridy++;
} }
c.gridx = 1;
c.gridwidth = 2;
granPanel.add(separateChannels = new JRadioButton("Separate channels"), c);
c.gridx += c.gridwidth;
granPanel.add(groupChannels = new JRadioButton("Group channels"), c);
separateChannels.setToolTipText("Use separate bins/encounters for each detection channel");
groupChannels.setToolTipText("Combine detections from different channels into the same bins/encounters");
ButtonGroup chanGroup = new ButtonGroup();
chanGroup.add(separateChannels);
chanGroup.add(groupChannels);
this.add(granPanel); this.add(granPanel);
// data selection // data selection
@ -127,8 +147,12 @@ public class GranularityCard extends ExportWizardCard {
private void enableControls() { private void enableControls() {
binLength.setEnabled(granularities[binnedIndex].isSelected()); binLength.setEnabled(granularities[binnedIndex].isSelected());
minCalls.setEnabled(granularities[binnedIndex].isSelected()); minBinnedCalls.setEnabled(granularities[binnedIndex].isSelected());
encounterGap.setEnabled(granularities[encounterIndex].isSelected()); encounterGap.setEnabled(granularities[encounterIndex].isSelected());
minEncounterCalls.setEnabled(granularities[encounterIndex].isSelected());
boolean binOrencount = granularities[binnedIndex].isSelected() | granularities[encounterIndex].isSelected();
separateChannels.setEnabled(binOrencount);
groupChannels.setEnabled(binOrencount);
} }
protected void newDataSelection() { protected void newDataSelection() {
@ -149,10 +173,9 @@ public class GranularityCard extends ExportWizardCard {
@Override @Override
public boolean getParams(StreamExportParams streamExportParams) { public boolean getParams(StreamExportParams streamExportParams) {
GranularityEnumType[] grans = GranularityEnumType.values(); for (int i = 0; i < allowedGranularities.length; i++) {
for (int i = 0; i < grans.length; i++) {
if (granularities[i].isSelected()) { if (granularities[i].isSelected()) {
streamExportParams.granularity = grans[i]; streamExportParams.granularity = allowedGranularities[i];
break; break;
} }
} }
@ -164,10 +187,10 @@ public class GranularityCard extends ExportWizardCard {
return detectionsExportWizard.showWarning("Invalid bin duration parameter"); return detectionsExportWizard.showWarning("Invalid bin duration parameter");
} }
try { try {
streamExportParams.minBinCount = Integer.valueOf(minCalls.getText()); streamExportParams.minBinCount = Integer.valueOf(minBinnedCalls.getText());
} }
catch (NumberFormatException e) { catch (NumberFormatException e) {
return detectionsExportWizard.showWarning("Invalid minimum call count"); return detectionsExportWizard.showWarning("Invalid minimum binned call count");
} }
} }
if (streamExportParams.granularity == GranularityEnumType.ENCOUNTER) { if (streamExportParams.granularity == GranularityEnumType.ENCOUNTER) {
@ -177,20 +200,30 @@ public class GranularityCard extends ExportWizardCard {
catch (NumberFormatException e) { catch (NumberFormatException e) {
return detectionsExportWizard.showWarning("Invalid encounter gap parameter"); return detectionsExportWizard.showWarning("Invalid encounter gap parameter");
} }
try {
streamExportParams.minEncounterCount = Integer.valueOf(minEncounterCalls.getText());
}
catch (NumberFormatException e) {
return detectionsExportWizard.showWarning("Invalid minimum encounter call count");
}
} }
streamExportParams.separateChannels = separateChannels.isSelected();
return streamExportParams.granularity != null; return streamExportParams.granularity != null;
} }
@Override @Override
public void setParams(StreamExportParams streamExportParams) { public void setParams(StreamExportParams streamExportParams) {
GranularityEnumType[] grans = GranularityEnumType.values(); for (int i = 0; i < granularities.length; i++) {
for (int i = 0; i < grans.length; i++) { granularities[i].setSelected(streamExportParams.granularity == allowedGranularities[i]);
granularities[i].setSelected(streamExportParams.granularity == grans[i]);
} }
binLength.setText(String.format("%3.1f", streamExportParams.binDurationS)); binLength.setText(String.format("%3.1f", streamExportParams.binDurationS));
minCalls.setText(String.format("%d", streamExportParams.minBinCount)); minBinnedCalls.setText(String.format("%d", streamExportParams.minBinCount));
encounterGap.setText(String.format("%3.1f", streamExportParams.encounterGapS)); encounterGap.setText(String.format("%3.1f", streamExportParams.encounterGapS));
minEncounterCalls.setText(String.format("%d", streamExportParams.minEncounterCount));
separateChannels.setSelected(streamExportParams.separateChannels);
groupChannels.setSelected(streamExportParams.separateChannels == false);
newDataSelection(); newDataSelection();
enableControls(); enableControls();
} }