/* *------------------------------------------------------------------------------ * Copyright (C) 2006-2014 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; import java.awt.Component; import java.io.InputStream; import java.io.OutputStream; import java.util.Collection; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.TreeMap; import org.jhotdraw.draw.AttributeKeys; import org.openmicroscopy.shoola.util.roi.exception.NoSuchROIException; import org.openmicroscopy.shoola.util.roi.exception.ParsingException; import org.openmicroscopy.shoola.util.roi.exception.ROICreationException; import org.openmicroscopy.shoola.util.roi.figures.MeasureLineFigure; import org.openmicroscopy.shoola.util.roi.figures.ROIFigure; import org.openmicroscopy.shoola.util.roi.io.ServerROIStrategy; import org.openmicroscopy.shoola.util.roi.io.XMLFileIOStrategy; import org.openmicroscopy.shoola.util.roi.model.ROI; import org.openmicroscopy.shoola.util.roi.model.ROICollection; import org.openmicroscopy.shoola.util.roi.model.ROIRelationship; import org.openmicroscopy.shoola.util.roi.model.ROIRelationshipList; import org.openmicroscopy.shoola.util.roi.model.ROIShape; import org.openmicroscopy.shoola.util.roi.model.ROIShapeRelationship; import org.openmicroscopy.shoola.util.roi.model.ROIShapeRelationshipList; import org.openmicroscopy.shoola.util.roi.model.ShapeList; import org.openmicroscopy.shoola.util.roi.model.annotation.MeasurementAttributes; import org.openmicroscopy.shoola.util.roi.model.util.Coord3D; import org.openmicroscopy.shoola.util.roi.model.util.MeasurementUnits; import org.openmicroscopy.shoola.util.ui.drawingtools.attributes.DrawingAttributes; import omero.gateway.model.ImageData; import omero.gateway.model.ROIData; import omero.gateway.model.ShapeSettingsData; import omero.model.Length; import omero.model.LengthI; import omero.model.enums.UnitsLength; /** * The ROI Component is the main interface to the object which control the * creations, storage, deletion and manipulation of ROIs. * * The ROIComponent also accesses(currently) the IOStrategy for loading and * saving ROIs. * * @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 ROIComponent extends Component { /** Flag indicating to check if the roi can be annotated.*/ public static final int ANNOTATE = 0; /** Flag indicating to check if the roi can be annotated.*/ public static final int DELETE = 1; /** Flag indicating to check if the roi can be annotated.*/ public static final int DELETE_MINE = 2; /** Flag indicating to check if the roi can be annotated.*/ public static final int DELETE_OTHERS = 3; /** Flag indicating to check if the roi can be deleted.*/ public static final int ALL = 4; /** Flag indicating to check if the roi can be edited.*/ public static final int EDIT = 5; /** The main object for storing and manipulating ROIs. */ private ROICollection roiCollection; /** The object used to load and save ROIs. */ private XMLFileIOStrategy ioStrategy; /** The object used to load and save ROIs. */ private ServerROIStrategy serverStrategy; /** Show the measurement units. */ private MeasurementUnits units; /** The map whose key is a file result ID and value a list of ROIs. */ private Map<Long, List<ROI>> roiResult; /** * Helper method to set the attributes of the newly created figure. * * @param fig The figure to handle. */ private void setFigureAttributes(ROIFigure fig) { AttributeKeys.FONT_SIZE.set(fig, new Double( ShapeSettingsData.DEFAULT_FONT_SIZE)); AttributeKeys.TEXT_COLOR.set(fig, ShapeSettingsData.DEFAULT_STROKE_COLOUR); AttributeKeys.STROKE_WIDTH.set(fig, ShapeSettingsData.DEFAULT_STROKE_WIDTH); MeasurementAttributes.SHOWID.set(fig, Boolean.valueOf(false)); MeasurementAttributes.SHOWMEASUREMENT.set(fig, Boolean.valueOf(fig instanceof MeasureLineFigure)); MeasurementAttributes.MEASUREMENTTEXT_COLOUR.set(fig, ShapeSettingsData.DEFAULT_STROKE_COLOUR); DrawingAttributes.SHOWTEXT.set(fig, Boolean.valueOf(false)); AttributeKeys.FILL_COLOR.set(fig, ShapeSettingsData.DEFAULT_FILL_COLOUR); AttributeKeys.STROKE_COLOR.set(fig, ShapeSettingsData.DEFAULT_STROKE_COLOUR); } /** * Helper method to set the annotations of the newly created shape. * * @param shape The shape to handle. */ private void setShapeAnnotations(ROIShape shape) { //ROIFigure fig = shape.getFigure(); // String type = fig.getType(); // if (type != null) AnnotationKeys.FIGURETYPE.set(shape, type); //ROIShape s = fig.getROIShape(); } /** * Creates a new instance. Initializes an collection to keep * track of the existing ROI. */ public ROIComponent() { roiCollection = new ROICollection(); units = new MeasurementUnits(new LengthI(1, UnitsLength.PIXEL), new LengthI(1, UnitsLength.PIXEL), new LengthI(1, UnitsLength.PIXEL)); roiResult = new LinkedHashMap<Long, List<ROI>>(); } /** Indicates to reset the identifier when loading from local file.*/ public void reset() { if (ioStrategy != null) ioStrategy.reset(); } /** * Removes the specified figure from the display. * * @param figure The figure to remove. * @throws NoSuchROIException If the ROI does not exist. */ void removeROI(ROIFigure figure) throws NoSuchROIException { if (figure == null) return; long id = figure.getROI().getID(); Coord3D coord = figure.getROIShape().getCoord3D(); deleteShape(id, coord); } /** * Creates a <code>ROI</code> from the passed figure. * * @param figure The figure to create the <code>ROI</code> from. * @param currentPlane The plane to add figure to. * @return Returns the created <code>ROI</code>. * @throws ROICreationException If the ROI cannot be created. * @throws NoSuchROIException If the ROI does not exist. */ ROI createROI(ROIFigure figure, Coord3D currentPlane) throws ROICreationException, NoSuchROIException { ROI roi = createROI(); ROIShape newShape = new ROIShape(roi, currentPlane, figure, figure.getBounds()); addShape(roi.getID(), currentPlane, newShape); return roi; } /** * Set the pixel sizes * * @param x Size of a pixel in x direction * @param y Size of a pixel in y direction * @param z Size of a pixel in z direction */ public void setPixelSizes(Length x, Length y, Length z) { units.setPixelSizes(x, y, z); } /** * Adds the specified figure to the display. * * @param figure The figure to add. * @param currentPlane The plane to add figure to. * @param addAttribs Passed <code>true</code> to add the attributes, * <code>false</code> otherwise. * @return returns the newly created ROI. * @throws NoSuchROIException * @throws ROICreationException */ public ROI addROI(ROIFigure figure, Coord3D currentPlane, boolean addAttribs) throws ROICreationException, NoSuchROIException { if (figure == null) throw new NullPointerException("Figure param null."); figure.setMeasurementUnits(units); if (addAttribs) setFigureAttributes(figure); ROI roi = null; roi = createROI(figure, currentPlane); if (roi == null) throw new ROICreationException("Unable to create ROI."); ROIShape shape = figure.getROIShape(); setShapeAnnotations(shape); return roi; } /** * Saves the current ROI data to passed stream. * * @param output The output stream to write the ROI into. * @throws ParsingException Thrown if an error occurs while creating the * XML element. */ public void saveROI(OutputStream output) throws ParsingException { if (output == null) throw new NullPointerException("No input stream specified."); if (ioStrategy == null) ioStrategy = new XMLFileIOStrategy(); ioStrategy.write(output, this); } /** * Converts the ROI in the component to ROIData and return. * * @param image The image the ROI are on. * @param index One of the constants defined by this class. * @param userID The id of the user currently logged in. * @return See above. * @throws Exception */ public List<ROIData> saveROI(ImageData image, int index, long userID) throws Exception { if (serverStrategy == null) serverStrategy = new ServerROIStrategy(); return serverStrategy.write(this, image, index, userID); } /** * Loads the ROIs. This method should be invoked straight after creating * the component. * * * @param input The stream with the previously saved ROIs or * <code>null</code> if no ROIs previously saved. * @return list of newly loaded ROI. * @throws ParsingException Thrown when an error occurred * while parsing the stream. * @throws NoSuchROIException Tried to access a ROI which does not * Exist. In this case most likely * reason is that a * LineConnectionFigure tried * to link to ROIShapes which have not * been created yet. * @throws ROICreationException Thrown while trying to create an * ROI. */ public List<ROI> loadROI(InputStream input) throws NoSuchROIException, ParsingException, ROICreationException { if (input == null) throw new NullPointerException("No input stream specified."); if (ioStrategy == null) ioStrategy = new XMLFileIOStrategy(); return ioStrategy.read(input, this); } /** * Reads the ROIs from the server and returns the UI representations. * * @param fileID The id of the file. * @param rois The collection of ROIs to convert. * @param userID The identifier of the user currently logged in. * @return See above. * @throws NoSuchROIException Tried to access a ROI which does not * Exist. In this case most likely * reason is that a * LineConnectionFigure tried * to link to ROIShapes which have not * been created yet. * @throws ROICreationException Thrown while trying to create an * ROI. */ public List<ROI> loadROI(long fileID, Collection rois, long userID) throws NoSuchROIException, ROICreationException { if (rois == null) throw new NullPointerException("No rois to transform."); if (serverStrategy == null) serverStrategy = new ServerROIStrategy(); List<ROI> l = serverStrategy.read(rois, this, userID); if (fileID > 0) roiResult.put(fileID, l); return l; } /** * Returns the list of ROIs associated to that file. * * @param fileID The id of the file. * @return See above. */ public List<ROI> getROIList(long fileID) { return roiResult.get(fileID); } /** * Generates the next ID for a new ROI. This method will possibly be * replaced with a call to the database for the generation of an ROI id. * * @return See above. */ public long getNextID() { return roiCollection.getNextID(); } /** * Creates a ROI with an ROI id == id. This method is called from the IO * strategy to create a pre-existing ROI from file. * * Note : if a ROI is created with the same ID the new ROI will replace the * old one. * * @param id The ROI id. * @return See above. * @throws ROICreationException If an error occurred while creating * an ROI, basic assumption is this is * linked to memory issues. */ public ROI createROI(long id) throws ROICreationException { return roiCollection.createROI(id, true, true, true, true); } /** * Creates a ROI with an ROI id == id. This method is called from the IO * strategy to create a pre-existing ROI from file. * * Note : if a ROI is created with the same ID the new ROI will replace the * old one. * * @param id The ROI id. * @param clientSideObject Is this object a client-side object. * @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. * @return See above. * @throws ROICreationException If an error occurred while creating * an ROI, basic assumption is this is * linked to memory issues. */ public ROI createROI(long id, boolean clientSideObject, boolean editable, boolean deletable, boolean annotatable) throws ROICreationException { return roiCollection.createROI(id, clientSideObject, editable, deletable, annotatable); } /** * Create a new ROI, assign it an ROI from the getNextID call. * * @return See above. * @throws ROICreationException If an error occurred while creating * an ROI, basic assumption is this is * linked to memory issues. */ public ROI createROI() throws ROICreationException { return roiCollection.createROI(); } /** * Create a new ROI, assign it an ROI from the getNextID call. * * @return See above. * @throws ROICreationException If an error occurred while creating * an ROI, basic assumption is this is * linked to memory issues. * @throws NoSuchROIException If the ROI to be cloned does not exist. */ public ROI cloneROI(long id) throws ROICreationException, NoSuchROIException { ROI newROI = roiCollection.createROI(); newROI.setAnnotations(roiCollection.getROI(id).getAnnotation()); return newROI; } /** * Returns the roiMap which is the TreeMap containing the ROI, ROI.id pairs. * It is an ordered Tree. * * @return See above. */ public TreeMap<Long, ROI> getROIMap() { return roiCollection.getROIMap(); } /** * Returns the ROI with the id == id. * This is obtained by a search of the ROIMap. * * @param id the ROI.id that is being requested. * @return See above. * @throws NoSuchROIException Thrown if a ROI.id does not exist. */ public ROI getROI(long id) throws NoSuchROIException { return roiCollection.getROI(id); } /** * Returns true if the roiComponent contains the ROI with id. * @param id see above. * @return see above. */ public boolean containsROI(long id) { return roiCollection.containsROI(id); } /** * Returns the ROIShape which is part of the ROI id, and exists on the plane * coordinates. * This method looks up the ROIIDMap (TreeMap) for the ROI with id * and then looks up that ROIs TreeMap for the ROIShape on the plane * coordinates. * * @param id The id of the ROI the ROIShape is a member of. * @param coord The plane where the ROIShape sits. * @return See Above. * @throws NoSuchROIException Thrown if a ROI.id does not exist. */ public ROIShape getShape(long id, Coord3D coord) throws NoSuchROIException { return roiCollection.getShape(id, coord); } /** * Returns the <code>true</code> if a shape is already associated to the * specified ROI at the passed coordinates. * * @param id The id of the ROI the ROIShape is a member of. * @param coord The plane where the ROIShape sits. * @return See Above. * @throws NoSuchROIException Thrown if a ROI.id does not exist. */ public boolean containsShape(long id, Coord3D coord) { if (!containsROI(id)) return false; try { ROI roi = getROI(id); return roi.containsShape(coord); } catch (Exception e) {} return false; } /** * Returns the list of ROIShapes which reside on the plane coordinates. * The ShapeList is an object which contains a TreeMap of the * ROIShapes and ROIId of those shapes. * * * @param coord The selected plane. * @return See above. * @throws NoSuchROIException Thrown if no shapes are on plane Coord. */ public ShapeList getShapeList(Coord3D coord) throws NoSuchROIException { return roiCollection.getShapeList(coord); } /** * Deletes the ROI and all its ROIShapes from the system. * * @param id The ROI to delete. * @throws NoSuchROIException Thrown if the ROI to be deleted does not * exist. */ public void deleteROI(long id) throws NoSuchROIException { roiCollection.deleteROI(id); } /** * Deletes the ROIShape from the ROI with id. * * @param id The ROI id which the ROIShape is a member. * @param coord The plane on which the ROIShape resides. * @throws NoSuchROIException Thrown if the ROI does not exist. */ public void deleteShape(long id, Coord3D coord) throws NoSuchROIException { roiCollection.deleteShape(id, coord); } /** * Adds a ROIShape to the ROI.id at coord. The ROIShape should be created * before hand. * * Note : if a shape already exist with ROI.id and Coord.coord it will * be replaced with the new ROIShape. * Note : if the shape is inserted in multiple places the system will behave * oddly. * * @param id The ROI id. * @param coord The selected plane. * @param shape The shape to add. * @throws ROICreationException Thrown if the ROIShape cannot be created. * @throws NoSuchROIException Thrown if the ROI, the ROIShape should be * added to, does not exist. */ public void addShape(long id, Coord3D coord, ROIShape shape) throws ROICreationException, NoSuchROIException { roiCollection.addShape(id, coord, shape); } /** * This method will create new versions of the ROIShape belonging to ROI.id * on plane coordinates and propagate it from plane start to end. * If the shape exists on a plane between start and end it will not be * overwritten. * * Note : iteration for planes occurs through z then t. * * @param id The ROI id. * @param selectedShape The plane where shape is to be duplicated from. * @param start The plane to propagate from. * @param end The plane to propagate to. * @throws NoSuchROIException Thrown if ROI with id does not exist. * @throws ROICreationException Thrown if the ROI cannot be created. */ public List<ROIShape> propagateShape(long id, Coord3D selectedShape, Coord3D start, Coord3D end) throws ROICreationException, NoSuchROIException { return roiCollection.propagateShape(id, selectedShape, start, end); } /** * Deletes the ROIShape belonging to ROI.id from plane start to plane end. * This method requires that the object belongs on all planes from start * to end, if it does not then it will throw an NoSuchShapeException. * * Note : iteration for planes occurs through z then t. * * @param id The ROI id. * @param start The start plane. * @param end The end plane. * @throws NoSuchROIException Thrown if no such ROI exists. */ public void deleteShape(long id, Coord3D start, Coord3D end) throws NoSuchROIException { roiCollection.deleteShape(id, start, end); } /** * Add an ROIRelationship to the system, the ROIRelationship will be parsed * to see what has to be setup to create relationships. * This is a relationship between ROIs. * * @param relationship The relation to add. */ public void addROIRelationship(ROIRelationship relationship) { roiCollection.addROIRelationship(relationship); } /** * Adds an ROIShapeRelationship to the system, the ROIShapeRelationship will * be parsed to see what has to be setup to create relationships. This is a * relationship between ROIShapes. * * @param relationship The relation to add. */ public void addROIShapeRelationship(ROIShapeRelationship relationship) { roiCollection.addROIShapeRelationship(relationship); } /** * Removes the ROIRelationship with id from the system. * * @param relationship The id of the relationship being removed. */ public void removeROIRelationship(long relationship) { roiCollection.removeROIRelationship(relationship); } /** * Removes the ROIShapeRelationship with id from the system. * * @param relationship The id of the relationship being removed. */ public void removeROIShapeRelationship(long relationship) { roiCollection.removeROIShapeRelationship(relationship); } /** * Return <code>true</code> if the ROIRelationship exists in the system, * <code>false</code> otherwise. * * @param relationship The id of the relationship. * @return See above. */ public boolean containsROIRelationship(long relationship) { return roiCollection.containsROIRelationship(relationship); } /** * Returns <code>true</code> if the ROIShapeRelationship exists * in the system, <code>false</code> otherwise. * * @param relationship The id of the relationship. * @return See above. */ public boolean containsROIShapeRelationship(long relationship) { return roiCollection.containsROIShapeRelationship(relationship); } /** * Returns the ROIRelationships which relate to ROI with id. * * @param roiID id to find relationships which belong to it. * @return see above. */ public ROIRelationshipList getROIRelationshipList(long roiID) { return roiCollection.getROIRelationshipList(roiID); } /** * Returns the ROIShapeRelationships which relate to ROIShape with ROI id. * * @param roiID id to find relationships which belong to it. * @return See above. */ public ROIShapeRelationshipList getROIShapeRelationshipList(long roiID) { return roiCollection.getROIShapeRelationshipList(roiID); } /** * Returns the measurement units of this component. * * @return See above. */ public MeasurementUnits getMeasurementUnits() { return units; } }