Tethys Localisation output

Not all types, but getting there.
This commit is contained in:
Douglas Gillespie 2024-08-05 17:58:08 +01:00
parent 36455153a2
commit 2929372533
10 changed files with 372 additions and 56 deletions

View File

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

View File

@ -1,16 +1,26 @@
eclipse.preferences.version=1
org.eclipse.jdt.core.codeComplete.argumentPrefixes=
org.eclipse.jdt.core.codeComplete.argumentSuffixes=
org.eclipse.jdt.core.codeComplete.fieldPrefixes=
org.eclipse.jdt.core.codeComplete.fieldSuffixes=
org.eclipse.jdt.core.codeComplete.localPrefixes=
org.eclipse.jdt.core.codeComplete.localSuffixes=
org.eclipse.jdt.core.codeComplete.staticFieldPrefixes=
org.eclipse.jdt.core.codeComplete.staticFieldSuffixes=
org.eclipse.jdt.core.codeComplete.staticFinalFieldPrefixes=
org.eclipse.jdt.core.codeComplete.staticFinalFieldSuffixes=
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.1
org.eclipse.jdt.core.compiler.codegen.targetPlatform=11
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
org.eclipse.jdt.core.compiler.compliance=1.3
org.eclipse.jdt.core.compiler.compliance=11
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
org.eclipse.jdt.core.compiler.debug.localVariable=generate
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
org.eclipse.jdt.core.compiler.problem.assertIdentifier=ignore
org.eclipse.jdt.core.compiler.problem.assertIdentifier=error
org.eclipse.jdt.core.compiler.problem.enablePreviewFeatures=disabled
org.eclipse.jdt.core.compiler.problem.enumIdentifier=ignore
org.eclipse.jdt.core.compiler.problem.enumIdentifier=error
org.eclipse.jdt.core.compiler.problem.forbiddenReference=warning
org.eclipse.jdt.core.compiler.problem.reportPreviewFeatures=warning
org.eclipse.jdt.core.compiler.release=disabled
org.eclipse.jdt.core.compiler.source=1.3
org.eclipse.jdt.core.compiler.source=11

View File

@ -0,0 +1,5 @@
eclipse.preferences.version=1
org.eclipse.jdt.ui.exception.name=e
org.eclipse.jdt.ui.gettersetter.use.is=true
org.eclipse.jdt.ui.keywordthis=false
org.eclipse.jdt.ui.overrideannotation=true

View File

@ -4,7 +4,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>org.pamguard</groupId>
<artifactId>Pamguard</artifactId>
<version>2.02.11d</version>
<version>2.02.12</version>
<name>Pamguard</name>
<description>Pamguard using Maven to control dependencies</description>
<url>www.pamguard.org</url>
@ -580,7 +580,7 @@
<dependency>
<groupId>com.fazecast</groupId>
<artifactId>jSerialComm</artifactId>
<version>2.5.3</version>
<version>2.11.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/edu.emory.mathcs/JTransforms -->

View File

@ -724,8 +724,16 @@ public class TethysControl extends PamControlledUnit implements PamSettings, Tet
*/
public void exportedDetections(PamDataBlock dataBlock) {
countProjectDetections();
// sendStateUpdate(new TethysState(StateType.NEWPAMGUARDSELECTION, Collection.Detections));
// sendStateUpdate(new TethysState(StateType.DELETEDATA));
sendStateUpdate(new TethysState(StateType.NEWPAMGUARDSELECTION, Collection.Detections));
}
public void deletedDetections(PamDataBlock dataBlock) {
countProjectDetections();
// sendStateUpdate(new TethysState(StateType.DELETEDATA));
sendStateUpdate(new TethysState(StateType.NEWPAMGUARDSELECTION, Collection.Detections));
}
/**
* @return the calibrationHandler
*/

View File

@ -1,5 +1,6 @@
package tethys.detection;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@ -30,6 +31,8 @@ import nilus.AlgorithmType;
import nilus.AlgorithmType.SupportSoftware;
import nilus.Localize.Effort;
import nilus.Localize.Effort.CoordinateReferenceSystem;
import nilus.Localize.Effort.ReferencedDocuments;
import nilus.Localize.Effort.ReferencedDocuments.Document;
import nilus.Localize.Localizations;
import nilus.DataSourceType;
import nilus.Deployment;
@ -49,6 +52,7 @@ import tethys.dbxml.DBXMLConnect;
import tethys.dbxml.TethysException;
import tethys.deployment.DeploymentHandler;
import tethys.localization.CoordinateName;
import tethys.localization.LocalizationHandler;
import tethys.localization.LocalizationSubType;
import tethys.localization.LocalizationType;
import tethys.localization.PLocalization;
@ -411,6 +415,8 @@ public class DetectionsHandler extends CollectionHandler {
DeploymentHandler depHandler = tethysControl.getDeploymentHandler();
ArrayList<PDeployment> deployments = depHandler.getMatchedDeployments();
LocalizationHandler localizationHandler = tethysControl.getLocalizationHandler();
/*
* The main documents for both dets and locs.
*/
@ -444,7 +450,7 @@ public class DetectionsHandler extends CollectionHandler {
exportObserver.update(prog);
granularityHandler.prepare(deployment.getAudioStart());
List<Detection> detectionList;
List<Detection> detectionList = null;
// export everything in that deployment.
// need to loop through all map points in this interval.
@ -467,19 +473,19 @@ public class DetectionsHandler extends CollectionHandler {
// onEffortDetections.
// }
}
else {
onEffortDetections = null;
detectionList = null;
}
// else {
// onEffortDetections = null;
// detectionList = null;
// }
if (localiseDocument == null && streamExportParams.exportLocalisations) {
localiseDocument = startLocalisationDocument(deployment, dataBlock, streamExportParams);
localiseDocument = startLocalisationDocument(deployment, detectionsDocument, dataBlock, streamExportParams);
Effort eff = localiseDocument.getEffort();
localiseDocument.getEffort().setStart(TethysTimeFuncs.xmlGregCalFromMillis(deployment.getAudioStart()));
localisations = localiseDocument.getLocalizations();
}
else {
localisations = null;
}
// else {
// localisations = null;
// }
if (mapPoint.getEndTime() < deployment.getAudioStart()) {
continue;
@ -495,17 +501,24 @@ public class DetectionsHandler extends CollectionHandler {
/*
* Here is where we need to handle the different granularities.
*/
Detection dets[] = granularityHandler.addDataUnit(dataUnit);
if (dets != null) {
for (int dd = 0; dd < dets.length; dd++) {
exportCount++;
documentCount++;
if (streamExportParams.exportDetections) {
if (streamExportParams.exportDetections) {
Detection dets[] = granularityHandler.addDataUnit(dataUnit);
if (dets != null) {
for (int dd = 0; dd < dets.length; dd++) {
exportCount++;
documentCount++;
detectionList.add(dets[dd]);
}
if (streamExportParams.exportLocalisations) {
// convert the dets into localisations and add them.
}
}
}
/**
* Localisations don't do granularity, so do all.
*/
if (streamExportParams.exportLocalisations) {
// convert the dets into localisations and add them.
nilus.LocalizationType localization = localizationHandler.createLocalization(localiseDocument, dataBlock, dataUnit, streamExportParams);
if (localization != null) {
localisations.getLocalization().add(localization);
}
}
@ -606,9 +619,9 @@ public class DetectionsHandler extends CollectionHandler {
}
}
prog = new DetectionExportProgress(null, null,totalMapPoints, totalMapPoints,
lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_COMPLETE);
exportObserver.update(prog);
// prog = new DetectionExportProgress(null, null,totalMapPoints, totalMapPoints,
// lastUnitTime, totalCount, exportCount, skipCount, DetectionExportProgress.STATE_COMPLETE);
// exportObserver.update(prog);
return DetectionExportProgress.STATE_COMPLETE;
}
@ -671,7 +684,7 @@ public class DetectionsHandler extends CollectionHandler {
return detections;
}
private Localize startLocalisationDocument(PDeployment deployment, PamDataBlock dataBlock,
private Localize startLocalisationDocument(PDeployment deployment, Detections detectionsDocument, PamDataBlock dataBlock,
StreamExportParams exportParams) {
Localize localisations = new Localize();
try {
@ -680,8 +693,9 @@ public class DetectionsHandler extends CollectionHandler {
e.printStackTrace();
return null;
}
if (localisations.getEffort() == null) {
Effort eff = new Effort();
Effort eff = localisations.getEffort();
if (eff == null) {
eff = new Effort();
try {
Helper.createRequiredElements(eff);
} catch (IllegalArgumentException | IllegalAccessException | InstantiationException e) {
@ -690,6 +704,30 @@ public class DetectionsHandler extends CollectionHandler {
}
localisations.setEffort(eff);
}
if (detectionsDocument != null) {
/*
* add the reference document information.
* Within PAMGuard, this will always be 1:1 with a Detections doc.
*/
ReferencedDocuments refDocs = eff.getReferencedDocuments();
if (refDocs == null) {
refDocs = new ReferencedDocuments();
try {
Helper.createRequiredElements(refDocs);
} catch (IllegalArgumentException | IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
eff.setReferencedDocuments(refDocs);
}
Document detectsDoc = new Document();
detectsDoc.setId(detectionsDocument.getId());
detectsDoc.setType(Collection.Detections.collectionName());
detectsDoc.setIndex(BigInteger.ZERO);
eff.getReferencedDocuments().getDocument().add(detectsDoc);
}
TethysDataProvider dataProvider = dataBlock.getTethysDataProvider(tethysControl);
String prefix = deployment.nilusObject.getId() + "_" + dataProvider.getDetectionsName();

View File

@ -1,24 +1,40 @@
package tethys.localization;
import java.math.BigInteger;
import java.util.ArrayList;
import PamDetection.AbstractLocalisation;
import PamDetection.LocContents;
import PamUtils.LatLong;
import PamUtils.PamUtils;
import PamguardMVC.PamDataBlock;
import nilus.CylindricalCoordinateType;
import PamguardMVC.PamDataUnit;
import nilus.AngularCoordinateType;
import nilus.BearingType;
import nilus.CoordinateType;
import nilus.Helper;
import nilus.LocalizationType;
import nilus.LocalizationType.References;
import nilus.LocalizationType.References.Reference;
import nilus.Localize;
import nilus.SpeciesIDType;
import nilus.Localize.Effort.CoordinateReferenceSystem;
import tethys.Collection;
import tethys.CollectionHandler;
import tethys.TethysControl;
import tethys.TethysTimeFuncs;
import tethys.detection.StreamDetectionsSummary;
import tethys.niluswraps.NilusDataWrapper;
import tethys.niluswraps.PDeployment;
import tethys.output.StreamExportParams;
import tethys.pamdata.AutoTethysProvider;
import tethys.species.DataBlockSpeciesManager;
import tethys.species.SpeciesMapItem;
public class LocalizationHandler extends CollectionHandler {
public LocalizationHandler(TethysControl tethysControl) {
super(tethysControl, Collection.Localizations);
// TODO Auto-generated constructor stub
}
// public LocalizationType getLoc() {
@ -75,4 +91,231 @@ public class LocalizationHandler extends CollectionHandler {
return null;
}
/**
* Create a Localization element object to add to a Localizations document.
* @param localiseDocument
* @param dataBlock
* @param dataUnit
* @param streamExportParams
* @return
*/
public LocalizationType createLocalization(Localize localiseDocument, PamDataBlock dataBlock, PamDataUnit dataUnit,
StreamExportParams streamExportParams) {
/*
* Get the current type from the document. This should always exist, so
* just go for it, then switch on the value to make the localisation.
*/
CoordinateReferenceSystem coordSystem = localiseDocument.getEffort().getCoordinateReferenceSystem();
String name = coordSystem.getName();
CoordinateName coordName = CoordinateName.valueOf(name);
if (coordName == null) {
return null;
}
LocalizationType locEl = null;
switch (coordName) {
case Cartesian:
locEl = createCartesianLoc(localiseDocument, dataBlock, dataUnit, streamExportParams);
break;
case Cylindrical:
locEl = createCylindricalLoc(localiseDocument, dataBlock, dataUnit, streamExportParams);
break;
case PerpindicularRange:
locEl = createPerpRange(localiseDocument, dataBlock, dataUnit, streamExportParams);
break;
case Polar:
locEl = createPolarLoc(localiseDocument, dataBlock, dataUnit, streamExportParams);
break;
case Range:
locEl = createRangeLoc(localiseDocument, dataBlock, dataUnit, streamExportParams);
break;
case Spherical:
locEl = createSphericalLoc(localiseDocument, dataBlock, dataUnit, streamExportParams);
break;
case UTM:
break;
case WGS84:
locEl = createWGS84Loc(localiseDocument, dataBlock, dataUnit, streamExportParams);
break;
default:
break;
}
return locEl;
}
private LocalizationType makeBaseLoc(PamDataBlock dataBlock, PamDataUnit dataUnit) {
LocalizationType locType = new LocalizationType();
try {
Helper.createRequiredElements(locType);
} catch (IllegalArgumentException | IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
locType.setTimeStamp(TethysTimeFuncs.xmlGregCalFromMillis(dataUnit.getTimeMilliseconds()));
DataBlockSpeciesManager spManager = dataBlock.getDatablockSpeciesManager();
if (spManager != null) {
SpeciesMapItem speciesStuff = spManager.getSpeciesItem(dataUnit);
SpeciesIDType species = new SpeciesIDType();
if (speciesStuff != null) {
species.setValue(BigInteger.valueOf(speciesStuff.getItisCode()));
locType.setSpeciesId(species);
}
}
References references = locType.getReferences();
if (references == null) {
references = new References();
try {
Helper.createRequiredElements(references);
} catch (IllegalArgumentException | IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
locType.setReferences(references);
}
Reference reference = new Reference();
reference.setIndex(BigInteger.valueOf(dataUnit.getUID()));
reference.setEventRef("UID");
locType.getReferences().getReference().add(reference);
return locType;
}
/**
* Get angle in degrees constrained to 0-360
* @param radians
* @return
*/
private double constrainRadianAngle(double radians) {
double deg = Math.toDegrees(radians);
if (Math.abs(deg) > 3600) {
return 359.9;
}
deg = PamUtils.constrainedAngle(deg);
deg = AutoTethysProvider.roundDecimalPlaces(deg, 2);
return deg;
}
private LocalizationType createWGS84Loc(Localize localiseDocument, PamDataBlock dataBlock, PamDataUnit dataUnit,
StreamExportParams streamExportParams) {
AbstractLocalisation loc = dataUnit.getLocalisation();
if (loc == null) {
return null;
}
LatLong latlong = loc.getLatLong(0);
if (latlong == null) {
return null;
}
LocalizationType locType = makeBaseLoc(dataBlock, dataUnit);
CoordinateType coord = new CoordinateType();
coord.setX(latlong.getLongitude());
coord.setY(latlong.getLatitude());
coord.setZ(latlong.getHeight());
locType.setCoordinate(coord);
return locType;
}
private LocalizationType createSphericalLoc(Localize localiseDocument, PamDataBlock dataBlock, PamDataUnit dataUnit,
StreamExportParams streamExportParams) {
// TODO Auto-generated method stub
return null;
}
private LocalizationType createRangeLoc(Localize localiseDocument, PamDataBlock dataBlock, PamDataUnit dataUnit,
StreamExportParams streamExportParams) {
// TODO Auto-generated method stub
return null;
}
private LocalizationType createBearingLoc(Localize localiseDocument, PamDataBlock dataBlock, PamDataUnit dataUnit,
StreamExportParams streamExportParams) {
AbstractLocalisation loc = dataUnit.getLocalisation();
if (loc == null) {
return null;
}
LocalizationType locType = makeBaseLoc(dataBlock, dataUnit);
double[] angles = loc.getAngles();
if (angles == null || angles.length == 0) {
return null;
}
BearingType angType = new BearingType();
angType.setAngle1(constrainRadianAngle(angles[0]));
if (angles.length >= 2) {
angType.setAngle2(constrainRadianAngle(angles[1]));
}
locType.setBearing(angType);
double[] angErr = loc.getAngleErrors();
if (angErr != null && angErr.length >= 1) {
BearingType angErrType = new BearingType();
angErrType.setAngle1(constrainRadianAngle(angErr[0]));
if (angErr.length >= 2) {
angErrType.setAngle2(constrainRadianAngle(angErr[1]));
}
locType.setBearingError(angErrType);
}
return locType;
}
private LocalizationType createPolarLoc(Localize localiseDocument, PamDataBlock dataBlock, PamDataUnit dataUnit,
StreamExportParams streamExportParams) {
AbstractLocalisation loc = dataUnit.getLocalisation();
if (loc == null) {
return null;
}
if (loc.hasLocContent(LocContents.HAS_RANGE) == false) {
// do the more basic bearing type instead.
return createBearingLoc(localiseDocument, dataBlock, dataUnit, streamExportParams);
}
LocalizationType locType = makeBaseLoc(dataBlock, dataUnit);
double[] angles = loc.getAngles();
if (angles == null || angles.length == 0) {
return null;
}
AngularCoordinateType angType = new AngularCoordinateType();
angType.setAngle1(constrainRadianAngle(angles[0]));
if (angles.length >= 2) {
angType.setAngle2(constrainRadianAngle(angles[1]));
}
if (loc.hasLocContent(LocContents.HAS_RANGE)) {
angType.setDistanceM(loc.getRange(0));
}
locType.setAngularCoordinate(angType);
double[] angErr = loc.getAngleErrors();
if (angErr != null && angErr.length >= 1) {
AngularCoordinateType angErrType = new AngularCoordinateType();
angErrType.setAngle1(constrainRadianAngle(angErr[0]));
if (angErr.length >= 2) {
angErrType.setAngle2(constrainRadianAngle(angErr[1]));
}
if (loc.hasLocContent(LocContents.HAS_RANGEERROR)) {
angErrType.setDistanceM(loc.getRangeError(0));
}
locType.setAngularCoordinateError(angErrType);
}
return locType;
}
private LocalizationType createPerpRange(Localize localiseDocument, PamDataBlock dataBlock, PamDataUnit dataUnit,
StreamExportParams streamExportParams) {
// TODO Auto-generated method stub
return null;
}
private LocalizationType createCylindricalLoc(Localize localiseDocument, PamDataBlock dataBlock,
PamDataUnit dataUnit, StreamExportParams streamExportParams) {
// TODO Auto-generated method stub
return null;
}
private LocalizationType createCartesianLoc(Localize localiseDocument, PamDataBlock dataBlock, PamDataUnit dataUnit,
StreamExportParams streamExportParams) {
// TODO Auto-generated method stub
return null;
}
}

View File

@ -102,22 +102,17 @@ abstract public class AutoTethysProvider implements TethysDataProvider {
@Override
public AlgorithmType getAlgorithm() {
/**
* Probably need to split this to provide detection algorithm parameters and
* localisation algorithm parameters, or pass in the document type as a function
* argument.
*/
AlgorithmType algorithm = new AlgorithmType();
try {
nilus.Helper.createRequiredElements(algorithm);
} catch (IllegalArgumentException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IllegalAccessException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (InstantiationException e) {
// TODO Auto-generated catch block
} catch (IllegalArgumentException | IllegalAccessException | InstantiationException e) {
e.printStackTrace();
}
// algorithm.setMethod(this.getAlgorithmMethod());
// algorithm.setSoftware("PAMGuard");
// algorithm.setVersion(PamguardVersionInfo.version);
nilus.AlgorithmType.Parameters algoParameters = this.getAlgorithmParameters();
if (algoParameters != null) {
algorithm.setParameters(algoParameters);

View File

@ -129,9 +129,13 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa
return mainPanel;
}
@Override
public void selectDataBlock(PamDataBlock dataBlock) {
if (this.dataBlock == dataBlock) {
/**
* update table for this datablock
* @param dataBlock datablock
* @param always always update, even if datablock hasn't changed.
*/
public void selectDataBlock(PamDataBlock dataBlock, boolean always) {
if (this.dataBlock == dataBlock && !always) {
return; // stops lots of requerying, which matters when database is large.
}
this.dataBlock = dataBlock;
@ -147,6 +151,11 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa
w.start();
}
@Override
public void selectDataBlock(PamDataBlock dataBlock) {
selectDataBlock(dataBlock, false);
}
@Override
public void taskFinished(String result) {
tableModel.fireTableDataChanged();
@ -169,13 +178,19 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa
@Override
public void updateState(TethysState tethysState) {
// boolean fullUpdate = needFullUpdate(tethysState);
if (dataBlock != null) {
PamDataBlock currBlock = dataBlock;
selectDataBlock(null);
selectDataBlock(dataBlock);
selectDataBlock(currBlock);
}
}
// private boolean needFullUpdate(TethysState tethysState) {
// // TODO Auto-generated method stub
// return false;
// }
private class MouseActions extends MouseAdapter {
@Override
@ -235,7 +250,7 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa
menuItem.addActionListener(new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
// deleteDocument(pDets);
deleteDocument(pDets);
}
});
popMenu.add(menuItem);
@ -305,13 +320,13 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa
@Override
public void taskFinished(Integer result) {
getTethysControl().exportedDetections(dataBlock);
selectDataBlock(dataBlock); // force table update.
getTethysControl().deletedDetections(dataBlock);
selectDataBlock(dataBlock, true); // force table update.
}
}
protected void deleteDocument(PDetections pDets) {
protected void deleteDocument(NilusDataWrapper pDets) {
String msg = String.format("Are you sure you want to delete the Detections document %s ?", pDets.getDocumentId());
int ans = WarnOnce.showWarning(PamGui.findComponentWindow(mainPanel), "Delete Document", msg, WarnOnce.OK_CANCEL_OPTION);
if (ans != WarnOnce.OK_OPTION) {
@ -322,8 +337,8 @@ public class DatablockDetectionsPanel extends TethysGUIPanel implements StreamTa
} catch (TethysException e) {
getTethysControl().showException(e);
}
getTethysControl().exportedDetections(dataBlock);
selectDataBlock(dataBlock); // force table update.
getTethysControl().deletedDetections(dataBlock);
selectDataBlock(dataBlock, true); // force table update.
}
private void displayDocument(NilusDataWrapper pDets) {

View File

@ -18,6 +18,7 @@ import PamView.panel.PamNorthPanel;
import PamguardMVC.PamDataBlock;
import tethys.TethysControl;
import tethys.TethysState;
import tethys.TethysState.StateType;
import tethys.TethysStateObserver;
import tethys.detection.DetectionExportObserver;
import tethys.detection.DetectionExportProgress;
@ -164,6 +165,7 @@ public class ExportWorkerCard extends ExportWizardCard implements DetectionExpor
progressText.setText("Export complete");
detectionsExportWizard.getCancelButton().setText("Close");
detectionsExportWizard.getPreviousButton().setEnabled(false);
// getTethysControl().sendStateUpdate(new TethysState(StateType.EXPORTRDATA));
break;
case DetectionExportProgress.STATE_WRITING:
progressText.setText("Writing to Tethys: " + progress.currentDetections.getId());