diff --git a/.omeroci/test-data b/.omeroci/test-data
index 0bbb4fc..32fb842 100755
--- a/.omeroci/test-data
+++ b/.omeroci/test-data
@@ -126,3 +126,11 @@ MAP2=$(omero obj new MapAnnotation)
omero obj map-set "$MAP2" mapValue testKey1 testValue2
omero obj map-set "$MAP2" mapValue testKey2 30
omero obj new ImageAnnotationLink parent="$IMAGE2" child="$MAP2"
+
+MAP3=$(omero obj new MapAnnotation)
+omero obj map-set "$MAP3" mapValue key value
+omero obj new ProjectAnnotationLink parent="$PROJECT2" child="$MAP3"
+omero obj new DatasetAnnotationLink parent="$DATASET3" child="$MAP3"
+omero obj new ScreenAnnotationLink parent="$SCREEN1" child="$MAP3"
+omero obj new PlateAnnotationLink parent="$PLATE1" child="$MAP3"
+omero obj new WellAnnotationLink parent="Well:1" child="$MAP3"
diff --git a/README.md b/README.md
index 920b4d0..40abc56 100644
--- a/README.md
+++ b/README.md
@@ -9,8 +9,8 @@ A plugin for ImageJ to provide macro extensions to access OMERO.
1. Install the [OMERO.insight plugin](https://omero-guides.readthedocs.io/en/latest/fiji/docs/installation.html) (if you
haven't already).
-2. Download the JAR file for this [library](https://github.com/GReD-Clermont/simple-omero-client/releases/tag/5.16.0/).
-3. Download the JAR file ([for this plugin](https://github.com/GReD-Clermont/omero_macro-extensions/releases/tag/1.3.3/)).
+2. Download the JAR file for this [library](https://github.com/GReD-Clermont/simple-omero-client/releases/tag/5.19.0/).
+3. Download the JAR file ([for this plugin](https://github.com/GReD-Clermont/omero_macro-extensions/releases/tag/1.4.0/)).
4. Place these JAR files in your plugins folder.
## How to use
@@ -121,6 +121,16 @@ tagName = Ext.getName("tag", tagIds[0]);
print(tagName);
```
+* Key-Value pairs:
+
+```
+kvpairs = Ext.list("kv-pairs");
+print(kvpairs);
+kvIds = split(kvpairs,",");
+kvName = Ext.getName("kvName", kvIds[0]);
+print(kvName);
+```
+
### Listing objects with a given name
It is also possible to list objects with a specific name:
@@ -185,6 +195,26 @@ tagName = Ext.getName("tag", tagIds[0]);
print(tagName);
```
+Key-Value pairs can also be listed for a given object:
+
+```
+pairs = Ext.list("kv-pairs", "image", imageIds[0]);
+print(pairs);
+pairIds = split(pairs,",");
+kvs = Ext.getName("kv-pair", pairIds[0]);
+print(kvs);
+```
+or:
+```
+kvpairs = Ext.getKeyValuePairs("image", imageIds[0]);
+print(kvpairs);
+```
+or for a given key:
+```
+values = Ext.getValues("image", imageIds[0], "key", defaultValue);
+print(values);
+```
+
Similarly, a dataset ID can be used to retrieve its images:
```
@@ -222,7 +252,7 @@ imageName = Ext.getName("image", imageIds[0]);
print(imageName);
```
-### Creating projects, datasets and tags
+### Creating projects, datasets, tags and key-value pairs
Projects can be created with *Ext.createProject*:
@@ -242,6 +272,12 @@ Tags can be created with *Ext.createTag*:
tagId = Ext.createTag(name, description);
```
+Key-value pairs can be created with *Ext.createKeyValuePair*:
+
+```
+pairId = Ext.createKeyValuePair(key, value);
+```
+
### Linking/unlinking objects
Objects can be linked with *Ext.link*, e.g.:
@@ -253,7 +289,7 @@ Ext.link("dataset", datasetId, "tag", tagId);
They can also be unlinked with *Ext.unlink*, e.g.:
```
-Ext.unlink("dataset", datasetId, "tag", tagId);
+Ext.unlink("dataset", datasetId, "kv-pair", pairId);
```
### Deleting objects
@@ -272,8 +308,16 @@ Pixel intensities can be retrieved from images:
imageplusID = Ext.getImage(imageIds[0]);
```
-Images can also be cropped on import by specifying the ROI as a String of the form:
-"x:xstart:xend,y:ystart:yend,c:cstart:cend,z:zstart:zend,t:tstart:tend".
+Images can also be cropped on import. In this case, image properties containing the position of the crop in the original
+image can be retrieved (IMAGE_POS_X, IMAGE_POS_Y, etc.).
+
+This crop can be done by specifying the ROI in one of two ways:
+1. using the ROI ID (as a String) from OMERO, for example:
+```
+imageplusID = Ext.getImage(imageIds[0], Roi.getProperty("ROI_ID"));
+```
+
+2. as a String of the form: "x:xstart:xend,y:ystart:yend,c:cstart:cend,z:zstart:zend,t:tstart:tend".
For example, cropping a 512x512x3x100x5 image starting at (0,0,0,50,3) and ending at (100,100,2,99,3) can be done with:
```
diff --git a/pom.xml b/pom.xml
index 123bd05..4356086 100644
--- a/pom.xml
+++ b/pom.xml
@@ -6,7 +6,7 @@
pom-scijava
org.scijava
- 37.0.0
+ 38.0.1
fr.igred
@@ -113,7 +113,7 @@
fr.igred
simple-omero-client
- 5.18.0
+ 5.19.0
diff --git a/src/main/java/fr/igred/ij/plugin/OMEROMacroExtension.java b/src/main/java/fr/igred/ij/plugin/OMEROMacroExtension.java
index 54d8ca8..d6c7770 100644
--- a/src/main/java/fr/igred/ij/plugin/OMEROMacroExtension.java
+++ b/src/main/java/fr/igred/ij/plugin/OMEROMacroExtension.java
@@ -18,6 +18,8 @@
import fr.igred.omero.Client;
import fr.igred.omero.GenericObjectWrapper;
+import fr.igred.omero.annotations.GenericAnnotationWrapper;
+import fr.igred.omero.annotations.MapAnnotationWrapper;
import fr.igred.omero.annotations.TableWrapper;
import fr.igred.omero.annotations.TagAnnotationWrapper;
import fr.igred.omero.exception.AccessException;
@@ -32,7 +34,6 @@
import fr.igred.omero.repository.PlateWrapper;
import fr.igred.omero.repository.ProjectWrapper;
import fr.igred.omero.repository.ScreenWrapper;
-import fr.igred.omero.repository.WellSampleWrapper;
import fr.igred.omero.repository.WellWrapper;
import fr.igred.omero.roi.ROIWrapper;
import ij.IJ;
@@ -58,9 +59,7 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
-import java.util.Comparator;
import java.util.HashMap;
-import java.util.TreeMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
@@ -85,6 +84,7 @@ public class OMEROMacroExtension implements PlugIn, MacroExtension {
private static final String PLATE = "plate";
private static final String WELL = "well";
private static final String TAG = "tag";
+ private static final String MAP = "kv-pair";
private static final String INVALID = "Invalid type";
private static final String ERROR_POSSIBLE_VALUES = "%s: %s. Possible values are: %s";
@@ -98,6 +98,7 @@ public class OMEROMacroExtension implements PlugIn, MacroExtension {
newDescriptor("createDataset", this, ARG_STRING, ARG_STRING, ARG_NUMBER + ARG_OPTIONAL),
newDescriptor("createProject", this, ARG_STRING, ARG_STRING),
newDescriptor("createTag", this, ARG_STRING, ARG_STRING),
+ newDescriptor("createKeyValuePair", this, ARG_STRING, ARG_STRING),
newDescriptor("link", this, ARG_STRING, ARG_NUMBER, ARG_STRING, ARG_NUMBER),
newDescriptor("unlink", this, ARG_STRING, ARG_NUMBER, ARG_STRING, ARG_NUMBER),
newDescriptor("addFile", this, ARG_STRING, ARG_NUMBER, ARG_STRING),
@@ -111,6 +112,7 @@ public class OMEROMacroExtension implements PlugIn, MacroExtension {
newDescriptor("delete", this, ARG_STRING, ARG_NUMBER),
newDescriptor("getName", this, ARG_STRING, ARG_NUMBER),
newDescriptor("getImage", this, ARG_NUMBER, ARG_STRING + ARG_OPTIONAL),
+ newDescriptor("getImageFromROI", this, ARG_NUMBER, ARG_NUMBER),
newDescriptor("getROIs", this, ARG_NUMBER, ARG_NUMBER + ARG_OPTIONAL, ARG_STRING + ARG_OPTIONAL),
newDescriptor("saveROIs", this, ARG_NUMBER, ARG_STRING + ARG_OPTIONAL),
newDescriptor("removeROIs", this, ARG_NUMBER, ARG_STRING + ARG_OPTIONAL),
@@ -129,6 +131,26 @@ public class OMEROMacroExtension implements PlugIn, MacroExtension {
private ExperimenterWrapper user = null;
+ /**
+ * Safely converts a String to a Long, returning null if it fails.
+ *
+ * @param s The string.
+ *
+ * @return The integer value represented by s, null if not applicable.
+ */
+ private static Long safeParseLong(String s) {
+ Long l = null;
+ if (s != null) {
+ try {
+ l = Long.parseLong(s);
+ } catch (NumberFormatException ignored) {
+ // DO NOTHING
+ }
+ }
+ return l;
+ }
+
+
/**
* Converts a list of GenericObjectWrappers to a comma-delimited list of IDs.
*
@@ -254,6 +276,41 @@ private static Bounds extractBounds(CharSequence bounds) {
}
+ /**
+ * Determines if the link between the referenced objects and annotations is invalid.
+ *
+ * @param objects The objects map (associating types and ids).
+ * @param annotations The annotations map (associating types and ids).
+ *
+ * @return True if the link is invalid, false otherwise.
+ */
+ private boolean isInvalidLink(Map objects,
+ Map annotations) {
+ Long projectId = objects.get(PROJECT);
+ Long imageId = objects.get(IMAGE);
+
+ Map hcs = new HashMap<>(3);
+ hcs.computeIfAbsent(SCREEN, objects::get);
+ hcs.computeIfAbsent(PLATE, objects::get);
+ hcs.computeIfAbsent(WELL, objects::get);
+
+ int nObjects = objects.values().size();
+ int nAnnotations = annotations.values().size();
+ int nHCS = hcs.values().size();
+
+ boolean linkNotTwo = nObjects + nAnnotations != 2;
+ boolean linkAnnotations = nAnnotations == 2;
+ boolean linkObjectHCS = nHCS >= 1 && nAnnotations == 0;
+
+ boolean linkProjectImage = imageId != null && projectId != null;
+
+ return linkNotTwo ||
+ linkAnnotations ||
+ linkObjectHCS ||
+ linkProjectImage;
+ }
+
+
/**
* Filters the objects list to only keep objects from the set user.
*
@@ -284,13 +341,9 @@ private > List filterUser(List list) {
private GenericObjectWrapper> getObject(String type, long id) {
String singularType = singularType(type);
- GenericObjectWrapper> object = null;
- if (singularType.equals(TAG)) {
- try {
- object = client.getTag(id);
- } catch (OMEROServerError | ServiceException e) {
- IJ.error("Could not retrieve tag: " + e.getMessage());
- }
+ GenericObjectWrapper> object;
+ if (TAG.equals(singularType) || MAP.equals(singularType)) {
+ object = getAnnotation(singularType, id);
} else {
object = getRepositoryObject(type, id);
}
@@ -298,6 +351,36 @@ private GenericObjectWrapper> getObject(String type, long id) {
}
+ /**
+ * Retrieves the annotation of the specified type with the specified ID.
+ *
+ * @param type The type of annotation.
+ * @param id The object ID.
+ *
+ * @return The object.
+ */
+ private GenericAnnotationWrapper> getAnnotation(String type, long id) {
+ String singularType = singularType(type);
+
+ GenericAnnotationWrapper> annotation = null;
+ try {
+ switch (singularType) {
+ case TAG:
+ annotation = client.getTag(id);
+ break;
+ case MAP:
+ annotation = client.getMapAnnotation(id);
+ break;
+ default:
+ IJ.error(INVALID + ": " + type + ".");
+ }
+ } catch (OMEROServerError | ServiceException | ExecutionException | AccessException e) {
+ IJ.error(String.format("Could not retrieve %s: %s", singularType, e.getMessage()));
+ }
+ return annotation;
+ }
+
+
/**
* Retrieves the repository object of the specified type with the specified ID.
*
@@ -334,7 +417,7 @@ private GenericRepositoryObjectWrapper> getRepositoryObject(String type, long
IJ.error(INVALID + ": " + type + ".");
}
} catch (ServiceException | AccessException | ExecutionException e) {
- IJ.error("Could not retrieve object: " + e.getMessage());
+ IJ.error(String.format("Could not retrieve %s: %s", singularType, e.getMessage()));
}
return object;
}
@@ -343,35 +426,35 @@ private GenericRepositoryObjectWrapper> getRepositoryObject(String type, long
/**
* Lists the objects of the specified type linked to a tag.
*
- * @param type The object type.
- * @param id The tag id.
+ * @param type The object type.
+ * @param annotation The annotation.
*
* @return A list of GenericObjectWrappers.
*/
- private List extends GenericObjectWrapper>> listForTag(String type, long id)
+ private List extends GenericObjectWrapper>> listForAnnotation(String type,
+ GenericAnnotationWrapper> annotation)
throws ServiceException, OMEROServerError, AccessException, ExecutionException {
String singularType = singularType(type);
List extends GenericObjectWrapper>> objects = new ArrayList<>(0);
- TagAnnotationWrapper tag = client.getTag(id);
switch (singularType) {
case PROJECT:
- objects = tag.getProjects(client);
+ objects = annotation.getProjects(client);
break;
case DATASET:
- objects = tag.getDatasets(client);
+ objects = annotation.getDatasets(client);
break;
case IMAGE:
- objects = tag.getImages(client);
+ objects = annotation.getImages(client);
break;
case SCREEN:
- objects = tag.getScreens(client);
+ objects = annotation.getScreens(client);
break;
case PLATE:
- objects = tag.getPlates(client);
+ objects = annotation.getPlates(client);
break;
case WELL:
- objects = tag.getWells(client);
+ objects = annotation.getWells(client);
break;
default:
String msg = String.format(ERROR_POSSIBLE_VALUES,
@@ -407,8 +490,11 @@ private List extends GenericObjectWrapper>> listForProject(String type, long
case TAG:
objects = project.getTags(client);
break;
+ case MAP:
+ objects = project.getMapAnnotations(client);
+ break;
default:
- IJ.error(String.format(ERROR_POSSIBLE_VALUES, INVALID, type, "datasets, images or tags."));
+ IJ.error(String.format(ERROR_POSSIBLE_VALUES, INVALID, type, "datasets, images, tags or kv-pairs."));
}
return objects;
}
@@ -435,8 +521,11 @@ private List extends GenericObjectWrapper>> listForDataset(String type, long
case TAG:
objects = dataset.getTags(client);
break;
+ case MAP:
+ objects = dataset.getMapAnnotations(client);
+ break;
default:
- IJ.error(String.format(ERROR_POSSIBLE_VALUES, INVALID, type, "images or tags."));
+ IJ.error(String.format(ERROR_POSSIBLE_VALUES, INVALID, type, "images, tags or kv-pairs."));
}
return objects;
}
@@ -455,37 +544,26 @@ private List extends GenericObjectWrapper>> listForScreen(String type, long
String singularType = singularType(type);
List extends GenericObjectWrapper>> objects = new ArrayList<>(0);
- ScreenWrapper screen = client.getScreen(id);
- List plates = screen.getPlates();
+
+ ScreenWrapper screen = client.getScreen(id);
switch (singularType) {
case PLATE:
- objects = plates;
+ objects = screen.getPlates();
break;
case WELL:
- List wells = new ArrayList<>();
- for (PlateWrapper plate : plates) {
- wells.addAll(plate.getWells(client));
- }
- wells.sort(Comparator.comparing(GenericObjectWrapper::getId));
- objects = wells;
+ objects = screen.getWells(client);
break;
case IMAGE:
- List images = new ArrayList<>();
- for (PlateWrapper plate : plates) {
- for (WellWrapper well : plate.getWells(client)) {
- images.addAll(well.getWellSamples().stream()
- .map(WellSampleWrapper::getImage)
- .collect(Collectors.toList()));
- }
- }
- images.sort(Comparator.comparing(GenericObjectWrapper::getId));
- objects = images;
+ objects = screen.getImages(client);
break;
case TAG:
objects = screen.getTags(client);
break;
+ case MAP:
+ objects = screen.getMapAnnotations(client);
+ break;
default:
- IJ.error(String.format(ERROR_POSSIBLE_VALUES, INVALID, type, "plates, wells, images or tags."));
+ IJ.error(String.format(ERROR_POSSIBLE_VALUES, INVALID, type, "plates, wells, images, tags or kv-pairs."));
}
return objects;
}
@@ -504,23 +582,80 @@ private List extends GenericObjectWrapper>> listForPlate(String type, long i
String singularType = singularType(type);
List extends GenericObjectWrapper>> objects = new ArrayList<>(0);
- PlateWrapper plate = client.getPlate(id);
+
+ PlateWrapper plate = client.getPlate(id);
switch (singularType) {
case WELL:
objects = plate.getWells(client);
break;
case IMAGE:
- objects = plate.getWells(client)
- .stream()
- .flatMap(w -> w.getWellSamples().stream())
- .map(WellSampleWrapper::getImage)
- .collect(Collectors.toList());
+ objects = plate.getImages(client);
break;
case TAG:
objects = plate.getTags(client);
break;
+ case MAP:
+ objects = plate.getMapAnnotations(client);
+ break;
default:
- IJ.error(String.format(ERROR_POSSIBLE_VALUES, INVALID, type, "wells, images or tags."));
+ IJ.error(String.format(ERROR_POSSIBLE_VALUES, INVALID, type, "wells, images, tags or kv-pairs."));
+ }
+ return objects;
+ }
+
+
+ /**
+ * Lists the objects of the specified type inside a well.
+ *
+ * @param type The object type.
+ * @param id The well id.
+ *
+ * @return A list of GenericObjectWrappers.
+ */
+ private List extends GenericObjectWrapper>> listForWell(String type, long id)
+ throws AccessException, ServiceException, ExecutionException {
+ String singularType = singularType(type);
+
+ List extends GenericObjectWrapper>> objects = new ArrayList<>(0);
+
+ WellWrapper well = client.getWell(id);
+ switch (singularType) {
+ case IMAGE:
+ objects = well.getImages();
+ break;
+ case TAG:
+ objects = well.getTags(client);
+ break;
+ case MAP:
+ objects = well.getMapAnnotations(client);
+ break;
+ default:
+ IJ.error(String.format(ERROR_POSSIBLE_VALUES, INVALID, type, "images, tags or kv-pairs."));
+ break;
+ }
+ return objects;
+ }
+
+
+ /**
+ * Lists the objects of the specified type linked to an image.
+ *
+ * @param type The object type.
+ * @param id The image id.
+ *
+ * @return A list of GenericObjectWrappers.
+ */
+ private List extends GenericObjectWrapper>> listForImage(String type, long id)
+ throws AccessException, ServiceException, ExecutionException {
+ String singularType = singularType(type);
+
+ List extends GenericObjectWrapper>> objects = new ArrayList<>(0);
+
+ ImageWrapper image = client.getImage(id);
+ if (TAG.equals(singularType)) {
+ objects = image.getTags(client);
+ } else {
+ IJ.error(String.format(ERROR_POSSIBLE_VALUES, INVALID, type, "tags."));
}
return objects;
}
@@ -585,8 +720,7 @@ public String downloadImage(long imageId, String path) {
List files = new ArrayList<>(0);
try {
files = client.getImage(imageId).download(client, path);
- } catch (ServiceException | AccessException | OMEROServerError | ExecutionException |
- NoSuchElementException e) {
+ } catch (ServiceException | AccessException | OMEROServerError | ExecutionException | NoSuchElementException e) {
IJ.error("Could not download image: " + e.getMessage());
}
return files.stream().map(File::toString).collect(Collectors.joining(","));
@@ -712,11 +846,15 @@ public void addToTable(String tableName, ResultsTable results, Long imageId, Lis
public void saveTableAsFile(String tableName, String path, CharSequence delimiter) {
TableWrapper table = tables.get(tableName);
- char sep = delimiter == null || delimiter.length() != 1 ? DEFAULT_DELIMITER : delimiter.charAt(0);
- try {
- table.saveAs(path, sep);
- } catch (FileNotFoundException | UnsupportedEncodingException e) {
- IJ.error("Could not create table file: ", e.getMessage());
+ if (table != null) {
+ char sep = delimiter == null || delimiter.length() != 1 ? DEFAULT_DELIMITER : delimiter.charAt(0);
+ try {
+ table.saveAs(path, sep);
+ } catch (FileNotFoundException | UnsupportedEncodingException e) {
+ IJ.error("Could not create table file: ", e.getMessage());
+ }
+ } else {
+ IJ.error("Table does not exist: " + tableName);
}
}
@@ -774,6 +912,27 @@ public long createTag(String name, String description) {
}
+ /**
+ * Creates a key-value pair on OMERO.
+ *
+ * @param key The key.
+ * @param value The value.
+ *
+ * @return The kv-pair ID.
+ */
+ public long createKeyValuePair(String key, String value) {
+ long id = -1;
+ try {
+ MapAnnotationWrapper pair = new MapAnnotationWrapper(key, value);
+ pair.saveAndUpdate(client);
+ id = pair.getId();
+ } catch (ServiceException | AccessException | ExecutionException e) {
+ IJ.error("Could not create kv-pair: " + e.getMessage());
+ }
+ return id;
+ }
+
+
/**
* Creates a project on OMERO.
*
@@ -883,6 +1042,10 @@ public String list(String type) {
List tags = client.getTags();
results = listToIDs(filterUser(tags));
break;
+ case MAP:
+ List maps = client.getMapAnnotations();
+ results = listToIDs(filterUser(maps));
+ break;
default:
String msg = String.format(ERROR_POSSIBLE_VALUES, INVALID, type,
"projects, datasets, images, screens, plates, wells or tags.");
@@ -940,6 +1103,10 @@ public String list(String type, String name) {
List tags = client.getTags(name);
results = listToIDs(filterUser(tags));
break;
+ case MAP:
+ List maps = client.getMapAnnotations(name);
+ results = listToIDs(filterUser(maps));
+ break;
default:
String msg = String.format(ERROR_POSSIBLE_VALUES, INVALID, type,
"projects, datasets, images, screens, plates, wells or tags.");
@@ -963,7 +1130,6 @@ public String list(String type, String name) {
*/
public String list(String type, String parent, long id) {
String singularParent = singularType(parent);
- String singularType = singularType(type);
Collection extends GenericObjectWrapper>> objects = new ArrayList<>(0);
try {
@@ -974,8 +1140,13 @@ public String list(String type, String parent, long id) {
case DATASET:
objects = listForDataset(type, id);
break;
+ case MAP:
+ MapAnnotationWrapper map = client.getMapAnnotation(id);
+ objects = listForAnnotation(type, map);
+ break;
case TAG:
- objects = listForTag(type, id);
+ TagAnnotationWrapper tag = client.getTag(id);
+ objects = listForAnnotation(type, tag);
break;
case SCREEN:
objects = listForScreen(type, id);
@@ -984,23 +1155,10 @@ public String list(String type, String parent, long id) {
objects = listForPlate(type, id);
break;
case WELL:
- WellWrapper well = client.getWell(id);
- if (IMAGE.equals(singularType)) {
- objects = well.getWellSamples().stream()
- .map(WellSampleWrapper::getImage)
- .collect(Collectors.toList());
- } else if (TAG.equals(singularType)) {
- objects = well.getTags(client);
- } else {
- IJ.error(String.format(ERROR_POSSIBLE_VALUES, INVALID, type, "images or tags."));
- }
+ objects = listForWell(type, id);
break;
case IMAGE:
- if (TAG.equals(singularType)) {
- objects = client.getImage(id).getTags(client);
- } else {
- IJ.error("Invalid type: " + type + ". Only possible value is: tags.");
- }
+ objects = listForImage(type, id);
break;
default:
String msg = String.format(ERROR_POSSIBLE_VALUES, INVALID, parent,
@@ -1059,23 +1217,34 @@ public void link(String type1, long id1, String type2, long id2) {
map.put(t1, id1);
map.put(t2, id2);
- Long projectId = map.get(PROJECT);
- Long datasetId = map.get(DATASET);
- Long imageId = map.get(IMAGE);
- Long tagId = map.get(TAG);
+ Map annMap = new HashMap<>(1);
+ annMap.computeIfAbsent(TAG, map::get);
+ annMap.computeIfAbsent(MAP, map::get);
+
+ Map objMap = new HashMap<>(2);
+ objMap.computeIfAbsent(PROJECT, map::get);
+ objMap.computeIfAbsent(DATASET, map::get);
+ objMap.computeIfAbsent(IMAGE, map::get);
+ objMap.computeIfAbsent(WELL, map::get);
+ objMap.computeIfAbsent(PLATE, map::get);
+ objMap.computeIfAbsent(SCREEN, map::get);
try {
- // Link tag to repository object
- if (t1.equals(TAG) ^ t2.equals(TAG)) {
- String obj = t1.equals(TAG) ? t2 : t1;
+ if (isInvalidLink(objMap, annMap)) {
+ IJ.error(String.format("Cannot link %s and %s", type1, type2));
+ } else if (annMap.size() == 1) { // Link annotation to repository object
+ String ann = annMap.keySet().iterator().next();
+ String obj = ann.equals(t1) ? t2 : t1;
GenericRepositoryObjectWrapper> object = getRepositoryObject(obj, map.get(obj));
if (object != null) {
- object.addTag(client, tagId);
+ object.link(client, getAnnotation(ann, annMap.get(ann)));
}
- } else if (datasetId == null || (projectId == null && imageId == null)) {
- IJ.error(String.format("Cannot link %s and %s", type1, type2));
} else { // Or link dataset to image or project
+ Long projectId = map.get(PROJECT);
+ Long datasetId = map.get(DATASET);
+ Long imageId = map.get(IMAGE);
+
DatasetWrapper dataset = client.getDataset(datasetId);
if (projectId != null) {
client.getProject(projectId).addDataset(client, dataset);
@@ -1105,23 +1274,34 @@ public void unlink(String type1, long id1, String type2, long id2) {
map.put(t1, id1);
map.put(t2, id2);
- Long projectId = map.get(PROJECT);
- Long datasetId = map.get(DATASET);
- Long imageId = map.get(IMAGE);
- Long tagId = map.get(TAG);
+ Map objMap = new HashMap<>(2);
+ objMap.computeIfAbsent(PROJECT, map::get);
+ objMap.computeIfAbsent(DATASET, map::get);
+ objMap.computeIfAbsent(IMAGE, map::get);
+ objMap.computeIfAbsent(WELL, map::get);
+ objMap.computeIfAbsent(PLATE, map::get);
+ objMap.computeIfAbsent(SCREEN, map::get);
+
+ Map annMap = new HashMap<>(1);
+ annMap.computeIfAbsent(TAG, map::get);
+ annMap.computeIfAbsent(MAP, map::get);
try {
- // Unlink tag from repository object
- if (t1.equals(TAG) ^ t2.equals(TAG)) {
- String obj = t1.equals(TAG) ? t2 : t1;
+ if (isInvalidLink(objMap, annMap)) {
+ IJ.error(String.format("Cannot unlink %s and %s", type1, type2));
+ } else if (annMap.size() == 1) { // Unlink annotation from repository object
+ String ann = annMap.keySet().iterator().next();
+ String obj = ann.equals(t1) ? t2 : t1;
GenericRepositoryObjectWrapper> object = getRepositoryObject(obj, map.get(obj));
if (object != null) {
- object.unlink(client, client.getTag(tagId));
+ object.unlink(client, getAnnotation(ann, annMap.get(ann)));
}
- } else if (datasetId == null || (projectId == null && imageId == null)) {
- IJ.error(String.format("Cannot unlink %s and %s", type1, type2));
} else { // Or unlink dataset from image or project
+ Long projectId = map.get(PROJECT);
+ Long datasetId = map.get(DATASET);
+ Long imageId = map.get(IMAGE);
+
DatasetWrapper dataset = client.getDataset(datasetId);
if (projectId != null) {
client.getProject(projectId).removeDataset(client, dataset);
@@ -1147,13 +1327,21 @@ public void unlink(String type1, long id1, String type2, long id2) {
* @return The object name.
*/
public String getName(String type, long id) {
- String name = null;
+ String name = "";
GenericObjectWrapper> object = getObject(type, id);
- if (object instanceof GenericRepositoryObjectWrapper>) {
- name = ((GenericRepositoryObjectWrapper>) object).getName();
- } else if (object instanceof TagAnnotationWrapper) {
- name = ((TagAnnotationWrapper) object).getName();
+ if (object != null) {
+ if (object instanceof GenericRepositoryObjectWrapper>) {
+ name = ((GenericRepositoryObjectWrapper>) object).getName();
+ } else if (object instanceof TagAnnotationWrapper) {
+ name = ((TagAnnotationWrapper) object).getName();
+ } else if (object instanceof MapAnnotationWrapper) {
+ MapAnnotationWrapper map = (MapAnnotationWrapper) object;
+ name = map.getContentAsEntryList()
+ .stream()
+ .map(e -> e.getKey() + "\t" + e.getValue())
+ .collect(Collectors.joining(String.format("%n")));
+ }
}
return name;
}
@@ -1163,20 +1351,30 @@ public String getName(String type, long id) {
* Opens an image with optional bounds. The bounds are in the form "x:min:max" with max included. Each of XYCZT is
* optional, min and max are also optional: "x:0:100 y::200 z:5: t::"
*
- * @param id The image ID.
- * @param bounds The XYCZT bounds
+ * @param id The image ID.
+ * @param roi The ROI ID or XYCZT bounds
*
* @return The image, as an {@link ImagePlus}.
*/
- public ImagePlus getImage(long id, CharSequence bounds) {
+ public ImagePlus getImage(long id, String roi) {
ImagePlus imp = null;
try {
ImageWrapper image = client.getImage(id);
- if (bounds == null) {
+ if (roi == null) {
imp = image.toImagePlus(client);
} else {
- Bounds b = extractBounds(bounds);
- imp = image.toImagePlus(client, b);
+ final Long roiId = safeParseLong(roi);
+ if (roiId != null) {
+ ROIWrapper oRoi = image.getROIs(client)
+ .stream()
+ .filter(r -> r.getId() == roiId)
+ .findFirst()
+ .orElseThrow(() -> new NoSuchElementException("ROI not found: " + roi));
+ imp = image.toImagePlus(client, oRoi);
+ } else {
+ Bounds b = extractBounds(roi);
+ imp = image.toImagePlus(client, b);
+ }
}
} catch (ServiceException | AccessException | ExecutionException | NoSuchElementException e) {
IJ.error("Could not retrieve image: " + e.getMessage());
@@ -1292,14 +1490,14 @@ public int saveROIs(ImagePlus imp, long id, String property) {
* @return The concatenated string of all key-value pairs for the specified repository object.
*/
public String getKeyValuePairs(String type, long id, String separator) {
- Map keyValuePairs = new TreeMap<>();
+ List> keyValuePairs = new ArrayList<>(0);
String sep = separator == null ? "\t" : separator;
GenericRepositoryObjectWrapper> object = getRepositoryObject(type, id);
try {
if (object != null) {
- keyValuePairs = new TreeMap<>(object.getKeyValuePairs(client));
+ keyValuePairs = object.getKeyValuePairsAsList(client);
}
} catch (ServiceException | AccessException | ExecutionException e) {
IJ.error("Could not retrieve object: " + e.getMessage());
@@ -1308,7 +1506,7 @@ public String getKeyValuePairs(String type, long id, String separator) {
int size = 10 * keyValuePairs.size();
StringBuilder concatenation = new StringBuilder(size);
- for (Map.Entry entry : keyValuePairs.entrySet()) {
+ for (Map.Entry entry : keyValuePairs) {
concatenation.append(entry.getKey())
.append(sep)
.append(entry.getValue())
@@ -1361,7 +1559,6 @@ public String getValue(String type, long id, String key, String defaultValue) {
* @return The number of ROIs that were deleted.
*/
public int removeROIs(long id) {
-
int removed = 0;
try {
ImageWrapper image = client.getImage(id);
@@ -1493,6 +1690,11 @@ public String handleExtension(String name, Object[] args) {
results = String.valueOf(tagId);
break;
+ case "createKeyValuePair":
+ long pairId = createKeyValuePair((String) args[0], (String) args[1]);
+ results = String.valueOf(pairId);
+ break;
+
case "addToTable":
tableName = (String) args[0];
String resultsName = (String) args[1];
@@ -1570,7 +1772,7 @@ public String handleExtension(String name, Object[] args) {
case "getImage":
id = ((Double) args[0]).longValue();
- ImagePlus imp = getImage(id, (CharSequence) args[1]);
+ ImagePlus imp = getImage(id, (String) args[1]);
if (imp != null) {
imp.show();
results = String.valueOf(imp.getID());
diff --git a/src/main/resources/helper.md b/src/main/resources/helper.md
index 81daa86..f2c9686 100644
--- a/src/main/resources/helper.md
+++ b/src/main/resources/helper.md
@@ -47,6 +47,7 @@ Ext.list(type, parentType, parentId)
Ext.getName(type, id)
> Gets the name of the specified object, given its `type` and `id`.
+> In the case of key-value pairs, it returns keys and values separated by tabs, one pair per line.
Ext.getImage(id)
> Opens the image with the given `id`.
@@ -54,9 +55,17 @@ Ext.getImage(id)
Ext.getImage(id, region)
> Opens a subregion of the image with the given `id`.
-> The region is specified as a string in the format "x:start:end,y:start:end,...".
+> The region is specified in one of two ways:
+> 1. using a ROI ID in OMERO
+> 2. as a string in the format "x:start:end,y:start:end,...".
> Returns the image ID in ImageJ.
+Ext.getKeyValuePairs(type, id)
+> Returns the key-value pairs attached to the object with the given `type` and `id`.
+
+Ext.getValue(type, id, key, defaultValue)
+> Returns the value for the given key attached to the object with the given `type` and `id`.
+
Ext.getROIs(imageId, toOverlay, property)
> Retrieves the ROIs for the image with the given `imageId`.
> These are added to the ROI manager by default.
@@ -74,6 +83,10 @@ Ext.getROIs(imageId, toOverlay, property)
### Saves data ###
+Ext.createKeyValuePair(key, value)
+> Creates a new key-value pair with the given `key` and `value`.
+> Returns the new pair ID.
+
Ext.createTag(name, description)
> Creates a new tag with the given `name` and `description`.
> Returns the new tag ID.
@@ -110,7 +123,7 @@ Ext.link(type1, id1, type2, id2)
> Possible types are:
> * Project and Dataset
> * Dataset and Image
-> * Tag and Project, Dataset or Image
+> * Tag or KV-pair and Project, Dataset, Screen, Plate, Well or Image
### Removes data on OMERO ###
diff --git a/src/test/java/fr/igred/ij/plugin/OMEROExtensionErrorTest.java b/src/test/java/fr/igred/ij/plugin/OMEROExtensionErrorTest.java
index 2ae669d..0ea5174 100644
--- a/src/test/java/fr/igred/ij/plugin/OMEROExtensionErrorTest.java
+++ b/src/test/java/fr/igred/ij/plugin/OMEROExtensionErrorTest.java
@@ -16,8 +16,10 @@
package fr.igred.ij.plugin;
+import ij.measure.ResultsTable;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Disabled;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.junit.jupiter.params.ParameterizedTest;
@@ -30,6 +32,7 @@
import java.io.InputStream;
import java.io.PrintStream;
import java.nio.file.Files;
+import java.util.ArrayList;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertTrue;
@@ -143,6 +146,15 @@ void testGetImageError() {
}
+ @Test
+ void testGetImageFromROIError() {
+ Object[] args = {1.0d, "-1"};
+ ext.handleExtension("getImage", args);
+ String expected = "Could not retrieve image: ROI not found: -1";
+ assertEquals(expected, outContent.toString().trim());
+ }
+
+
@Test
void testListForUserError() {
Object[] args = {"hello"};
@@ -188,6 +200,7 @@ void testListInvalidArgs() {
"hello;plate;2",
"hello;well;2",
"hello;tag;2",
+ "hello;kv-pair;2",
"hello;TestDatasetImport;",
"hello;;",})
void testListInvalidType(String type1, String type2, Double id) {
@@ -199,8 +212,20 @@ void testListInvalidType(String type1, String type2, Double id) {
}
+ @Test
+ void testGetNameInvalidType() {
+ String type = "hello";
+ Object[] args = {type, 1.0};
+ String output = ext.handleExtension("getName", args);
+ String error = outContent.toString().trim();
+ assertTrue(output.isEmpty());
+ assertTrue(error.startsWith("Invalid type: hello."));
+ }
+
+
@ParameterizedTest
@ValueSource(strings = {"link", "unlink"})
+ @Disabled("Methods no longer try to link or unlink invalid types currently")
void testLinkUnlinkInvalidType(String function) {
Object[] args = {"tag", 1.0, "hello", 1.0};
ext.handleExtension(function, args);
@@ -210,22 +235,61 @@ void testLinkUnlinkInvalidType(String function) {
@ParameterizedTest
- @ValueSource(strings = {"link", "unlink"})
- void testCannotLinkOrUnlink(String function) {
- Object[] args = {"hello", 1.0, "world", 1.0};
+ @CsvSource(delimiter = ';', value = {"link;hello;1.0;world;1.0",
+ "unlink;hello;1.0;world;1.0",
+ "link;image;image",
+ "link;image;project",
+ "link;image;screen",
+ "link;tag;kv-pair"})
+ void testCannotLinkOrUnlink(String function, String type1, String type2) {
+ Object[] args = {type1, 1.0, type2, 1.0};
ext.handleExtension(function, args);
- String expected = String.format("Cannot %s hello and world", function);
+ String expected = String.format("Cannot %s %s and %s", function, type1, type2);
assertEquals(expected, outContent.toString().trim());
}
+
@Test
void testKeyNotExist() {
- final String key = "notExist";
+ final String key = "notExist";
final double imageId = 2;
- Object[] args = {"image", imageId, key, null};
+ Object[] args = {"image", imageId, key, null};
ext.handleExtension("getValue", args);
String expected = "Could not retrieve value: Key \"" + key + "\" not found";
assertEquals(expected, outContent.toString().trim());
}
+
+ @Test
+ void testClearTable() throws Exception {
+ long imageId = 1L;
+ String label1 = "test";
+ String label2 = "test2";
+ double size1 = 25.023579d;
+ double size2 = 50.0d;
+
+ ResultsTable rt1 = new ResultsTable();
+ rt1.incrementCounter();
+ rt1.setLabel(label1, 0);
+ rt1.setValue("Size", 0, size1);
+
+ ResultsTable rt2 = new ResultsTable();
+ rt2.incrementCounter();
+ rt2.setLabel(label2, 0);
+ rt2.setValue("Size", 0, size2);
+
+ ext.addToTable("test_table", rt1, imageId, new ArrayList<>(0), null);
+ ext.addToTable("test_table", rt2, imageId, new ArrayList<>(0), null);
+
+ Object[] args2 = {"test_table"};
+ ext.handleExtension("clearTable", args2);
+
+ File textFile = new File("test.txt");
+ Object[] args3 = {"test_table", textFile.getCanonicalPath(), null};
+ ext.handleExtension("saveTableAsFile", args3);
+
+ String expected = "Table does not exist: test_table";
+ assertEquals(expected, outContent.toString().trim());
+ }
+
}
diff --git a/src/test/java/fr/igred/ij/plugin/OMEROExtensionTest.java b/src/test/java/fr/igred/ij/plugin/OMEROExtensionTest.java
index a1227f8..7bf2418 100644
--- a/src/test/java/fr/igred/ij/plugin/OMEROExtensionTest.java
+++ b/src/test/java/fr/igred/ij/plugin/OMEROExtensionTest.java
@@ -117,7 +117,9 @@ void testListForUser(String user, double output) {
"list;plates;1,2,3",
"list;wells;1,2,3,4,5",
"list;tags;1,2,3",
- "list;tag;1,2,3",})
+ "list;tag;1,2,3",
+ "list;kv-pairs;4,5,6",
+ "list;kv-pair;4,5,6",})
void testListAll(String extension, String type, String output) {
Object[] args = {type, null, null};
String result = ext.handleExtension(extension, args);
@@ -135,7 +137,9 @@ void testListAll(String extension, String type, String output) {
"list;screen;TestScreen;1",
"list;plate;Plate Name 0;1,2",
"list;tags;tag2;2",
- "list;tag;tag2;2",})
+ "list;tag;tag2;2",
+ "list;kv-pairs;testKey1;4,5",
+ "list;kv-pair;testKey1;4,5",})
void testListByName(String extension, String type, String name, String output) {
Object[] args = {type, name, null};
String result = ext.handleExtension(extension, args);
@@ -161,9 +165,16 @@ void testListByName(String extension, String type, String name, String output) {
"list;screens;tag;1.0;1",
"list;plates;tag;1.0;1",
"list;wells;tag;1.0;1",
+ "list;tags;project;2.0;1",
+ "list;tags;dataset;3.0;1",
"list;tags;screen;1.0;1",
"list;tags;plate;1.0;1",
"list;tags;well;1.0;1",
+ "list;kv-pairs;project;2.0;6",
+ "list;kv-pairs;dataset;3.0;6",
+ "list;kv-pairs;screen;1.0;6",
+ "list;kv-pairs;plate;1.0;6",
+ "list;kv-pairs;well;1.0;6",
"list;plates;screen;2.0;2,3",
"list;wells;screen;2.0;2,3,4,5",
"list;images;screen;1.0;5,6",
@@ -174,7 +185,8 @@ void testListFrom(String extension, String type, String parent, double id, Strin
Object[] args = {type, parent, id};
String result = ext.handleExtension(extension, args);
- String sortedIds = Arrays.stream(result.split(",")).map(Long::parseLong).sorted()
+ String sortedIds = Arrays.stream(result.split(","))
+ .map(Long::parseLong).sorted()
.map(String::valueOf)
.collect(Collectors.joining(","));
@@ -192,7 +204,8 @@ void testListFrom(String extension, String type, String parent, double id, Strin
"getName;screen;1.0;TestScreen",
"getName;plate;2.0;Plate Name 0",
"getName;well;1.0;Well A-1",
- "getName;tag;1.0;tag1",})
+ "getName;tag;1.0;tag1",
+ "getName;kv-pair;6.0;key\tvalue",})
void testGetName(String extension, String type, double id, String output) {
Object[] args = {type, id};
String result = ext.handleExtension(extension, args);
@@ -213,7 +226,7 @@ void testCreateProject() {
@ParameterizedTest
@NullSource
- @ValueSource(doubles = {1.0})
+ @ValueSource(doubles = 1.0)
void testCreateDataset(Double projectId) {
Object[] args = {"toDelete", "toBeDeleted", projectId};
String result = ext.handleExtension("createDataset", args);
@@ -238,12 +251,31 @@ void testCreateAndLinkTag() {
}
+ @Test
+ void testCreateAndLinkKVPair() {
+ final double projectId = 2;
+ Object[] args = {"TestKey", "TestValue"};
+ String result = ext.handleExtension("createKeyValuePair", args);
+ Double id = Double.parseDouble(result);
+ Object[] args2 = {"kv-pair", id, "project", projectId};
+ ext.handleExtension("link", args2);
+ Object[] args3 = {"kv-pair", id};
+ ext.handleExtension("delete", args3);
+ assertNotNull(id);
+ }
+
+
@ParameterizedTest
- @CsvSource(delimiter = ';', value = {"tag;1.0;project;2.0",
+ @CsvSource(delimiter = ';', value = {"project;2.0;tag;1.0",
"tag;1.0;dataset;3.0",
"tag;1.0;screen;1.0",
"tag;1.0;plate;1.0",
"tag;1.0;well;1.0",
+ "project;2.0;kv-pair;6.0",
+ "kv-pair;6.0;dataset;3.0",
+ "kv-pair;6.0;screen;1.0",
+ "kv-pair;6.0;plate;1.0",
+ "kv-pair;6.0;well;1.0",
"dataset;3.0;project;2.0",
"image;1.0;dataset;1.0",})
void testUnlinkThenLink(String type1, double id1, String type2, double id2) {
@@ -326,10 +358,10 @@ void testGetImage() {
@ParameterizedTest
@ValueSource(strings = {"x:100:200 y:1:511", "x:50:150 y:2:512", "x:50:150 y:2:513"})
- void testGetImageTwoBounds(CharSequence bounds) {
+ void testGetImageTwoBounds(String bounds) {
final int partSize = 100;
final int fullSize = 510;
- ImagePlus imp = ext.getImage(1L, bounds);
+ ImagePlus imp = ext.getImage(1L, bounds);
assertEquals(partSize, imp.getWidth());
assertEquals(fullSize, imp.getHeight());
}
@@ -337,7 +369,7 @@ void testGetImageTwoBounds(CharSequence bounds) {
@ParameterizedTest
@ValueSource(strings = {"x:100: y::412", "X:100: Y::412", "x::412 y:100:"})
- void testGetImageOneBound(CharSequence bounds) {
+ void testGetImageOneBound(String bounds) {
final int size = 412;
ImagePlus imp = ext.getImage(1L, bounds);
assertEquals(size, imp.getWidth());
@@ -347,13 +379,13 @@ void testGetImageOneBound(CharSequence bounds) {
@ParameterizedTest
@ValueSource(strings = {"x:300:480 y:24:36 z:1:3 c:0:4 t:3:6", "X:300:480 Y:24:36 Z:1:3 C:0:4 T:3:6"})
- void testGetImageAllBounds(CharSequence bounds) {
+ void testGetImageAllBounds(String bounds) {
final int sizeX = 180;
final int sizeY = 12;
final int sizeZ = 2;
final int sizeC = 4;
final int sizeT = 3;
- ImagePlus imp = ext.getImage(1L, bounds);
+ ImagePlus imp = ext.getImage(1L, bounds);
assertEquals(sizeX, imp.getWidth());
assertEquals(sizeY, imp.getHeight());
assertEquals(sizeZ, imp.getNSlices());
@@ -364,12 +396,12 @@ void testGetImageAllBounds(CharSequence bounds) {
@ParameterizedTest
@ValueSource(strings = {"", " ", "x: y:: ", "x::9999 y:0:9999 z:50:99 c::99 t::99", "^#azerty*$"})
- void testGetImageNoBounds(CharSequence bounds) {
- final int size = 512;
+ void testGetImageNoBounds(String bounds) {
+ final int size = 512;
final int sizeZ = 3;
final int sizeC = 5;
final int sizeT = 7;
- ImagePlus imp = ext.getImage(1L, bounds);
+ ImagePlus imp = ext.getImage(1L, bounds);
assertEquals(size, imp.getWidth());
assertEquals(size, imp.getHeight());
assertEquals(sizeZ, imp.getNSlices());
@@ -380,12 +412,12 @@ void testGetImageNoBounds(CharSequence bounds) {
@ParameterizedTest
@ValueSource(strings = {"z:0", "z:2", "Z:0", "z:0:1"})
- void testGetImageZ(CharSequence bounds) {
- final int size = 512;
+ void testGetImageZ(String bounds) {
+ final int size = 512;
final int sizeZ = 1;
final int sizeC = 5;
final int sizeT = 7;
- ImagePlus imp = ext.getImage(1L, bounds);
+ ImagePlus imp = ext.getImage(1L, bounds);
assertEquals(size, imp.getWidth());
assertEquals(size, imp.getHeight());
assertEquals(sizeZ, imp.getNSlices());
@@ -396,12 +428,12 @@ void testGetImageZ(CharSequence bounds) {
@ParameterizedTest
@ValueSource(strings = {"c:0", "c:2", "C:0", "c:0:1"})
- void testGetImageC(CharSequence bounds) {
- final int size = 512;
+ void testGetImageC(String bounds) {
+ final int size = 512;
final int sizeZ = 3;
final int sizeC = 1;
final int sizeT = 7;
- ImagePlus imp = ext.getImage(1L, bounds);
+ ImagePlus imp = ext.getImage(1L, bounds);
assertEquals(size, imp.getWidth());
assertEquals(size, imp.getHeight());
assertEquals(sizeZ, imp.getNSlices());
@@ -412,12 +444,12 @@ void testGetImageC(CharSequence bounds) {
@ParameterizedTest
@ValueSource(strings = {"t:0", "t:2", "T:0", "t:0:1"})
- void testGetImageT(CharSequence bounds) {
- final int size = 512;
+ void testGetImageT(String bounds) {
+ final int size = 512;
final int sizeZ = 3;
final int sizeC = 5;
final int sizeT = 1;
- ImagePlus imp = ext.getImage(1L, bounds);
+ ImagePlus imp = ext.getImage(1L, bounds);
assertEquals(size, imp.getWidth());
assertEquals(size, imp.getHeight());
assertEquals(sizeZ, imp.getNSlices());
@@ -427,7 +459,7 @@ void testGetImageT(CharSequence bounds) {
@Test
- void testSaveAndGetROIs() {
+ void testGetImageFromROI() {
ImagePlus imp = ext.getImage(1L, null);
Overlay overlay = new Overlay();
Roi roi = new Roi(25, 30, 70, 50);
@@ -436,10 +468,43 @@ void testSaveAndGetROIs() {
imp.setOverlay(overlay);
int savedROIs = ext.saveROIs(imp, 1L, "");
overlay.clear();
+ imp.setOverlay(null);
int loadedROIs = ext.getROIs(imp, 1L, true, "");
+
+ Roi retrievedRoi = imp.getOverlay().get(0);
+ String roiId = retrievedRoi.getProperty("ROI_ID");
+ ImagePlus cropped = ext.getImage(1L, roiId);
+
ext.removeROIs(1L);
int clearedROIs = ext.getROIs(imp, 1L, true, "");
+ assertEquals(1, savedROIs);
+ assertEquals(1, loadedROIs);
+ assertEquals(0, clearedROIs);
+ assertEquals(25, Integer.parseInt(cropped.getProp("IMAGE_POS_X")));
+ assertEquals(30, Integer.parseInt(cropped.getProp("IMAGE_POS_Y")));
+ assertEquals(70, cropped.getWidth());
+ assertEquals(50, cropped.getHeight());
+ }
+
+
+ @Test
+ void testSaveAndGetROIs() {
+ ImagePlus imp = ext.getImage(1L, null);
+ Overlay overlay = new Overlay();
+ Roi roi = new Roi(25, 30, 70, 50);
+ roi.setImage(imp);
+ overlay.add(roi);
+ imp.setOverlay(overlay);
+
+ int savedROIs = ext.saveROIs(imp, 1L, "");
+ overlay.clear();
+ int loadedROIs = ext.getROIs(imp, 1L, true, "");
+
+ Object[] args = {1.0d};
+ ext.handleExtension("removeROIs", args);
+
+ int clearedROIs = ext.getROIs(imp, 1L, true, "");
assertEquals(1, savedROIs);
assertEquals(1, loadedROIs);
@@ -452,7 +517,7 @@ void testSaveAndGetROIs() {
@CsvSource(delimiter = ';', value = {"image;1;null;testKey1\ttestValue1\ttestKey2\t20",
"image;3;' ';testKey1 testValue1 testKey2 20",
"image;2;&&;testKey1&&testValue2&&testKey2&&30",
- "image;4;'';''"}, nullValues = {"null"})
+ "image;4;'';''"}, nullValues = "null")
void testGetKeyValuePairs(String type, Double id, String separator, String output) {
Object[] args = {type, id, separator};
String result = ext.handleExtension("getKeyValuePairs", args);
@@ -464,7 +529,7 @@ void testGetKeyValuePairs(String type, Double id, String separator, String outpu
@CsvSource(delimiter = ';', value = {"image;1;testKey1;null;testValue1",
"image;3;testKey2;null;20",
"image;2;testKey2;null;30",
- "image;2;notExist;default;default"}, nullValues = {"null"})
+ "image;2;notExist;default;default"}, nullValues = "null")
void testGetValue(String type, Double id, String key, String defaultValue, String output) {
Object[] args = {type, id, key, defaultValue};
String result = ext.handleExtension("getValue", args);
@@ -585,6 +650,7 @@ void testTable() throws Exception {
client.connect(HOSTNAME, (int) PORT, USERNAME, PASSWORD.toCharArray());
List tables = client.getDataset(1L).getTables(client);
client.disconnect();
+
assertEquals(1, tables.size());
assertEquals(2, tables.get(0).getRowCount());
assertEquals(size1, tables.get(0).getData(0, 2));