/* * org.openmicroscopy.shoola.util.roi.model.ROI * *------------------------------------------------------------------------------ * Copyright (C) 2006-2007 University of Dundee. All rights reserved. * * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. * *------------------------------------------------------------------------------ */ package org.openmicroscopy.shoola.util.roi.model; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import java.util.TreeMap; import org.openmicroscopy.shoola.util.roi.exception.NoSuchROIException; import org.openmicroscopy.shoola.util.roi.exception.ROICreationException; import org.openmicroscopy.shoola.util.roi.figures.ROIFigure; import org.openmicroscopy.shoola.util.roi.model.ROIShape; import org.openmicroscopy.shoola.util.roi.model.annotation.AnnotationKey; import org.openmicroscopy.shoola.util.roi.model.annotation.AnnotationKeys; import org.openmicroscopy.shoola.util.roi.model.attachment.Attachment; import org.openmicroscopy.shoola.util.roi.model.attachment.AttachmentKey; import org.openmicroscopy.shoola.util.roi.model.attachment.AttachmentMap; import org.openmicroscopy.shoola.util.roi.model.util.Coord3D; /** * The ROI object. * * @author Jean-Marie Burel      * <a href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a> * @author Donald MacDonald      * <a href="mailto:donald@lifesci.dundee.ac.uk">donald@lifesci.dundee.ac.uk</a> * @version 3.0 * @since OME3.0 */ public class ROI { /** Default size of the ROI Map. */ final static int DEFAULTMAPSIZE = 101; /** The id of the ROI. */ private long id; /** Flag indicating the figure can/cannot be deleted.*/ private boolean deletable; /** Flag indicating the figure can/cannot be annotated.*/ private boolean annotatable; /** Flag indicating the figure can/cannot be edited.*/ private boolean editable; /** Is the object a server-side or client-side object. */ private boolean clientSide; /** The TreeMap containing the ROI shapes of the ROI. */ private TreeMap<Coord3D, ROIShape> roiShapes; /** The Attachments in the ROI. */ private AttachmentMap attachments; /** Annotations in the ROI. */ private Map<AnnotationKey, Object> annotations = new HashMap<AnnotationKey,Object>(); /** * Forbidden annotations can't be set by the setAnnotation() operation. * They can only be changed by basicSetAnnotations(). */ private Set<AnnotationKey> forbiddenAnnotations; /** The identifier of the owner. */ private long ownerID; /** * Initializes the ROI with id and construct the TreeMap to contain * the ROIShapes of the ROI and there mapping the coord3D they exist on. * * @param id id of the ROI. * @param shape The ROIShape being constructed with the ROI. * @param editable Flag indicating the figure can/cannot be edited. * @param deletable Flag indicating the figure can/cannot be deleted. * @param annotatable Flag indicating the figure can/cannot be annotated. */ private void init(long id, boolean clientSide, boolean editable, boolean deletable, boolean annotatable) { ownerID = -1; this.id = id; this.clientSide = clientSide; this.deletable = deletable; this.annotatable = annotatable; this.editable = editable; roiShapes = new TreeMap<Coord3D, ROIShape>(new Coord3D()); attachments = new AttachmentMap(); } /** * Construct the ROI with id. * @param id see above. * @param clientSide * @param editable Flag indicating the figure can/cannot be edited. * @param deletable Flag indicating the figure can/cannot be deleted. * @param annotatable Flag indicating the figure can/cannot be annotated. */ public ROI(long id, boolean clientSide, boolean editable, boolean deletable, boolean annotatable) { init(id, clientSide, editable, deletable, annotatable); } /** * Constructs the ROI with id on coordinate and initial ROIShape shape. * * @param id The ID of the ROI. * @param coord The coordinate of the ROIShape being constructed with the * ROI. * @param shape The ROIShape being constructed with the ROI. * @param editable Flag indicating the figure can/cannot be edited. * @param deletable Flag indicating the figure can/cannot be deleted. * @param annotatable Flag indicating the figure can/cannot be annotated. */ public ROI(long id, boolean clientSide, Coord3D coord, ROIShape shape, boolean editable, boolean deletable, boolean annotatable) { init(id, clientSide, editable, deletable, annotatable); roiShapes.put(coord, shape); } /** * Add an attachment to the ROI. * @param key the key of the attachment. * @param attachment the value of the attachment. */ public void addAttachment(AttachmentKey key, Attachment attachment) { attachments.addAttachment(key, attachment); } /** * Get the attachment on the ROi with key * @param key see above. * @return see above. */ public Attachment getAttachment(AttachmentKey key) { return attachments.getAttachment(key); } /** * Get the map of all attachments. * @return see above. */ public AttachmentMap getAttachmentMap() { return attachments; } /** * Gets the ROI id. * * @return see above. */ public long getID() { return id; } /** * Returns <code>true</code> if the object a clientSide object. * * @return See above. */ public boolean isClientSide() { return clientSide; } /** * Gets the range of the T sections this ROI spans. * * @return string. see above. */ public String getTRange() { Coord3D low = roiShapes.firstKey(); Coord3D high = roiShapes.lastKey(); int s = low.getTimePoint(); int e = high.getTimePoint(); if (s < 0 || e < 0) return ""; s++; e++; if (s == e) return ""+s; return s+"-"+e; } /** * Gets the range of the z-sections this ROI spans. * * @return string. see above. */ public String getZRange() { Coord3D low = roiShapes.firstKey(); Coord3D high = roiShapes.lastKey(); int s = low.getZSection(); int e = high.getZSection(); if (s < 0 || e < 0) return ""; s++; e++; if (s == e) return ""+s; return s+"-"+e; } /** * Gets the range of the shapes this ROI contains. * * @return string. see above. */ public String getShapeTypes() { HashMap<String,Integer> shapeTypes = new HashMap<String, Integer>(); Iterator<ROIShape> shapeIterator = roiShapes.values().iterator(); ROIShape shape; String type; while (shapeIterator.hasNext()) { shape = shapeIterator.next(); type = shape.getFigure().getType(); if (shapeTypes.containsKey(type)) { int value = shapeTypes.get(type)+1; shapeTypes.put(type, value); } else shapeTypes.put(type, Integer.valueOf(1)); } Iterator<String> typeIterator = shapeTypes.keySet().iterator(); boolean first = true; StringBuffer buffer = new StringBuffer(); while (typeIterator.hasNext()) { type = typeIterator.next(); if (!first) { buffer.append(","); first = false; } buffer.append(type); } return buffer.toString(); } /** * Returns <code>true</code> if this ROI's roiShapes visible, * <code>false</code> otherwise. * * @return See above. */ public boolean isVisible() { boolean visible = false; Iterator<ROIShape> shapeIterator = roiShapes.values().iterator(); ROIShape shape; while (shapeIterator.hasNext()) { shape = shapeIterator.next(); visible = visible | shape.getFigure().isVisible(); } return visible; } /** * Return <code>true</code> if the ROI contains a ROIShape on coordinates, * <code>false</code> otherwise. * * @param coord see above. * @return see above. */ public boolean containsShape(Coord3D coord) { return roiShapes.containsKey(coord); } /** * Return <code>true</code> if the ROI contains a ROIShape on [start, end], * <code>false</code> otherwise. * * @param start see above. * @param end see above. * @return see above. */ public boolean containsShape(Coord3D start, Coord3D end) { //for(int c = start.c; c < end.c ; c++) for (int t = start.getTimePoint(); t < end.getTimePoint() ; t++) for (int z = start.getZSection(); z < end.getZSection() ; z++) if (!roiShapes.containsKey(new Coord3D(z, t))) return false; return true; } /** * Gets the TreeMap containing the ROIShapes. * * @return see above. */ public TreeMap<Coord3D, ROIShape> getShapes() { return roiShapes; } /** * Gets the ROIShape on plane coordinates. * * @param coord see above. * @return see above. * @throws NoSuchROIException Throw exception if ROI has no ROIShape on * coordinates. */ public ROIShape getShape(Coord3D coord) throws NoSuchROIException { if (!roiShapes.containsKey(coord)) throw new NoSuchROIException("ROI " + id + " does not contain " + "ROIShape on Coord " + coord.toString()); return roiShapes.get(coord); } /** * Gets the figure on plane coordinates. * * @param coord see above. * @return see above. * @throws NoSuchROIException Throw exception if ROI has no ROIShape on * coordinates. */ public ROIFigure getFigure(Coord3D coord) throws NoSuchROIException { if (!roiShapes.containsKey(coord)) throw new NoSuchROIException("ROI " + id + " does not contain " + "ROIShape on Coord " + coord.toString()); return getShape(coord).getFigure(); } /** * Returns all the figures. * * @return See above. */ public List<ROIFigure> getAllFigures() { List<ROIFigure> figures = new ArrayList<ROIFigure>(); Collection<ROIShape> set = roiShapes.values(); Iterator<ROIShape> i = set.iterator(); while (i.hasNext()) { figures.add(i.next().getFigure()); } return figures; } /** * Adds ROIShape shape to the ROI. If the ROI already has a shape at * coordinates an exception will be thrown. * @param shape see above. * @throws ROICreationException see above. */ public void addShape(ROIShape shape) throws ROICreationException { if (roiShapes.containsKey(shape.getCoord3D())) throw new ROICreationException(); roiShapes.put(shape.getCoord3D(), shape); } /** * Deletes the ROIShape on coordinates from the ROI. * * @param coord see above. * @throws NoSuchROIException Throw exception if the ROI does not contain * an ROIShape on plane coordinates. */ public void deleteShape(Coord3D coord) throws NoSuchROIException { if (!roiShapes.containsKey(coord)) throw new NoSuchROIException("ROI " + id + " does not contain " + "ROIShape on Coord " + coord.toString()); roiShapes.remove(coord); } /** * Sets the value off the annotation with key. * * @param key see above. * @param newValue see above. */ public void setAnnotation(AnnotationKey key, Object newValue) { if (forbiddenAnnotations == null || ! forbiddenAnnotations.contains(key)) { Object oldValue = annotations.get(key); if (!annotations.containsKey(key) || oldValue != newValue || oldValue != null && newValue != null && ! oldValue.equals(newValue)) { basicSetAnnotation(key, newValue); } } } /** * Sets an annotation to be enabled if the passed value is * <code>true</code>. * * @param key see above. * @param b see above. */ public void setAnnotationEnabled(AnnotationKey key, boolean b) { if (forbiddenAnnotations == null) { forbiddenAnnotations = new HashSet<AnnotationKey>(); } if (b) { forbiddenAnnotations.remove(key); } else { forbiddenAnnotations.add(key); } } /** * Return <code>true</code> if the annotation with key is allowed, * <code>false</code> otherwise. * * @param key see above. * @return see above. */ public boolean isAnnotationEnabled(AnnotationKey key) { return forbiddenAnnotations == null || ! forbiddenAnnotations.contains(key); } /** * Sets the map with the elements of the map. * * @param map see above. */ public void basicSetAnnotations(Map<AnnotationKey, Object> map) { for (Map.Entry<AnnotationKey, Object> entry : map.entrySet()) { basicSetAnnotation(entry.getKey(), entry.getValue()); } } /** * Sets the annotation map of the ROI to map * @param map see above. */ public void setAnnotations(Map<AnnotationKey, Object> map) { for (Map.Entry<AnnotationKey, Object> entry : map.entrySet()) { setAnnotation(entry.getKey(), entry.getValue()); } } /** * Returns the annotation map for the ROI. * * @return see above. */ public Map<AnnotationKey, Object> getAnnotation() { return new HashMap<AnnotationKey,Object>(annotations); } /** * Sets an annotation of the ROI. * AnnotationKey name and semantics are defined by the class implementing * the ROI interface. */ public void basicSetAnnotation(AnnotationKey key, Object newValue) { if (forbiddenAnnotations == null || ! forbiddenAnnotations.contains(key)) { annotations.put(key, newValue); } } /** * Returns <code>true</code> if the key has an annotation, * <code>false</code> otherwise. * * @param key The key to handle. * @return See above. */ public boolean hasAnnotation(String key) { Iterator<AnnotationKey> i = annotations.keySet().iterator(); AnnotationKey annotationKey; while (i.hasNext()) { annotationKey = i.next(); if (annotationKey.getKey().equals(key)) return true; } return false; } /** * Gets an annotation from the ROI. * @return annotation. */ public Object getAnnotation(AnnotationKey key) { return hasAnnotation(key) ? annotations.get(key) : key.getDefaultValue(); } /** * Returns the annotation key for element string. * * @param name see above. * @return see above. */ protected AnnotationKey getAnnotationKey(String name) { return AnnotationKeys.supportedAnnotationMap.get(name); } /** * Applies all annotation of this ROI to that ROI. * @param that the ROIShape to get annotation from. */ protected void applyAnnotationsTo(ROIShape that) { for (Map.Entry<AnnotationKey, Object> entry : annotations.entrySet()) { that.setAnnotation(entry.getKey(), entry.getValue()); } } /** * Removes annotation with key. * * @param key see above. */ public void removeAnnotation(AnnotationKey key) { if (hasAnnotation(key)) { //Object oldValue = getAnnotation(key); annotations.remove(key); } } /** * Return <code>true</code> if the ROI has the an annotation with key, * <code>false</code> otherwise. * * @param key the key of the annotation. * @return see above. */ public boolean hasAnnotation(AnnotationKey key) { return annotations.containsKey(key); } /** * Sets the identifier of the owner. * * @param ownerID the identifier of the owner. */ public void setOwnerID(long ownerID) { this.ownerID = ownerID; } /** * Returns the identifier of the owner. * * @return See above. */ public long getOwnerID() { return ownerID; } /** * Indicates if the ROI can be annotated if <code>true</code>, * <code>false</code> otherwise. * * @return See above. */ public boolean canAnnotate() { return annotatable; } /** * Indicates if the ROI can be deleted if <code>true</code>, * <code>false</code> otherwise. * * @return See above. */ public boolean canDelete() { return deletable; } /** * Indicates if the ROI can be edited if <code>true</code>, * <code>false</code> otherwise. * * @return See above. */ public boolean canEdit() { return editable; } }