/* * Copyright 2006-2014 University of Dundee. All rights reserved. * Use is subject to license terms supplied in LICENSE.txt */ package ome.logic; import java.util.ArrayList; import java.util.Iterator; import java.util.List; import org.springframework.transaction.annotation.Transactional; import ome.annotations.RolesAllowed; import ome.api.IPixels; import ome.api.ServiceInterface; import ome.conditions.ValidationException; import ome.model.IObject; import ome.model.core.Channel; import ome.model.core.Image; import ome.model.core.LogicalChannel; import ome.model.core.Pixels; import ome.model.display.RenderingDef; import ome.model.enums.DimensionOrder; import ome.model.enums.PixelsType; import ome.model.stats.StatsInfo; import ome.parameters.Parameters; import ome.system.EventContext; import ome.util.PixelData; /** * implementation of the Pixels service interface. * * @author Jean-Marie Burel      <a * href="mailto:j.burel@dundee.ac.uk">j.burel@dundee.ac.uk</a> * @author <br> * Andrea Falconi      <a * href="mailto:a.falconi@dundee.ac.uk"> a.falconi@dundee.ac.uk</a> * @version 2.2 <small> (<b>Internal version:</b> $Revision$ $Date: * 2005/06/12 23:27:31 $) </small> * @since OME2.2 */ @Transactional(readOnly = true) public class PixelsImpl extends AbstractLevel2Service implements IPixels { /** * Returns the interface this implementation is for. * @see AbstractLevel2Service#getServiceInterface() */ public Class<? extends ServiceInterface> getServiceInterface() { return IPixels.class; } /** Standard rendering definition HQL query prefix */ public static final String RENDERING_DEF_QUERY_PREFIX = "select rdef from RenderingDef as rdef " + "left outer join fetch rdef.details.owner " + "left outer join fetch rdef.quantization " + "left outer join fetch rdef.model " + "left outer join fetch rdef.waveRendering as cb " + "left outer join fetch cb.family " + "left outer join fetch rdef.spatialDomainEnhancement where "; // ~ Service methods // ========================================================================= @RolesAllowed("user") public Pixels retrievePixDescription(long pixId) { Pixels p = iQuery.findByQuery("select p from Pixels as p " + "left outer join fetch p.pixelsType as pt " + "left outer join fetch p.channels as c " + "left outer join fetch c.logicalChannel as lc " + "left outer join fetch c.statsInfo " + "left outer join fetch lc.photometricInterpretation " + "left outer join fetch lc.illumination " + "left outer join fetch lc.mode " + "left outer join fetch lc.contrastMethod " + "where p.id = :id", new Parameters().addId(pixId)); return p; } @Override @RolesAllowed("user") public RenderingDef retrieveRndSettingsFor(long pixId, long userId) { List<IObject> list = retrieveAllRndSettings(pixId, userId); if (list == null || list.size() == 0) return null; return (RenderingDef) list.get(0); } @Override @RolesAllowed("user") public List<IObject> retrieveAllRndSettings(long pixId, long userId) { Parameters params = new Parameters(); params.addLong("p_id", pixId); String restriction = "rdef.pixels.id = :p_id "; if (userId >= 0) { restriction += "and rdef.details.owner.id = :o_id "; params.addLong("o_id", userId); } List<IObject> l = iQuery.findAllByQuery( RENDERING_DEF_QUERY_PREFIX +restriction + "order by rdef.details.updateEvent.time desc", params); return l; } @Override @RolesAllowed("user") public RenderingDef retrieveRndSettings(long pixId) { Long userId = sec.getEffectiveUID(); RenderingDef rd = retrieveRndSettingsFor(pixId, userId); if (rd == null) { final EventContext ec = this.sec.getEventContext(false); final Pixels pixelsObj = this.iQuery.get(Pixels.class, pixId); final boolean isGraphCritical = this.sec.isGraphCritical(pixelsObj.getDetails()); long pixOwner = pixelsObj.getDetails().getOwner().getId(); long currentUser = ec.getCurrentUserId(); if (currentUser != pixOwner) { rd = retrieveRndSettingsFor(pixId, pixOwner); } } return rd; } @Override @RolesAllowed("user") public RenderingDef loadRndSettings(long renderingDefId) { return (RenderingDef) iQuery.findByQuery( RENDERING_DEF_QUERY_PREFIX + "rdef.id = :id", new Parameters().addId(renderingDefId)); } /** Actually performs the work declared in {@link #copyAndResizePixels()}. */ private Pixels _copyAndResizePixels(long pixelsId, Integer sizeX, Integer sizeY, Integer sizeZ, Integer sizeT, List<Integer> channelList, String methodology, boolean copyStats) { Pixels from = retrievePixDescription(pixelsId); Pixels to = new Pixels(); // Ensure we have no values out of bounds outOfBoundsCheck(sizeX, "sizeX"); outOfBoundsCheck(sizeY, "sizeY"); outOfBoundsCheck(sizeZ, "sizeZ"); outOfBoundsCheck(sizeT, "sizeT"); // Check that the channels in the list are valid indexes. if(channelList!=null) channelOutOfBounds(channelList, "channel", from); // Copy basic metadata to.setDimensionOrder(from.getDimensionOrder()); to.setMethodology(methodology); to.setPhysicalSizeX(from.getPhysicalSizeX()); to.setPhysicalSizeY(from.getPhysicalSizeY()); to.setPhysicalSizeZ(from.getPhysicalSizeZ()); to.setPixelsType(from.getPixelsType()); to.setRelatedTo(from); to.setSizeX(sizeX != null? sizeX : from.getSizeX()); to.setSizeY(sizeY != null? sizeY : from.getSizeY()); to.setSizeZ(sizeZ != null? sizeZ : from.getSizeZ()); to.setSizeT(sizeT != null? sizeT : from.getSizeT()); to.setSizeC(channelList!= null ? channelList.size() : from.getSizeC()); to.setSha1("Pending..."); // Copy channel data, if the channel list is null copy all channels. // or copy the channels in the channelList if it's not null. if(channelList != null) { for (Integer channel : channelList) { copyChannel(channel, from, to, copyStats); } } else { for (int channel = 0 ; channel < from.sizeOfChannels(); channel++) { copyChannel(channel, from, to, copyStats); } } return to; } @RolesAllowed("user") @Transactional(readOnly = false) public Long copyAndResizePixels(long pixelsId, Integer sizeX, Integer sizeY, Integer sizeZ, Integer sizeT, List<Integer> channelList, String methodology, boolean copyStats) { Pixels from = retrievePixDescription(pixelsId); Pixels to = _copyAndResizePixels(pixelsId, sizeX, sizeY, sizeZ, sizeT, channelList, methodology, copyStats); // Deal with Image linkage Image image = from.getImage(); image.addPixels(to); // Save and return our newly created Pixels Id image = iUpdate.saveAndReturnObject(image); return image.getPixels(image.sizeOfPixels() - 1).getId(); } @RolesAllowed("user") @Transactional(readOnly = false) public Long copyAndResizeImage(long imageId, Integer sizeX, Integer sizeY, Integer sizeZ, Integer sizeT, List<Integer> channelList, String name, boolean copyStats) { Image iFrom = iQuery.get(Image.class, imageId); Image iTo = new Image(); // Set the image name iTo.setAcquisitionDate(iFrom.getAcquisitionDate()); iTo.setName(name); iTo.setObjectiveSettings(iFrom.getObjectiveSettings()); iTo.setImagingEnvironment(iFrom.getImagingEnvironment()); iTo.setExperiment(iFrom.getExperiment()); iTo.setStageLabel(iFrom.getStageLabel()); iTo.setInstrument(iFrom.getInstrument()); // Copy each Pixels set that the source image has Iterator<Pixels> i = iFrom.iteratePixels(); while (i.hasNext()) { Pixels p = i.next(); Pixels to = _copyAndResizePixels(p.getId(), sizeX, sizeY, sizeZ, sizeT, channelList, null, copyStats); iTo.addPixels(to); } // Save and return our newly created Image Id iTo = iUpdate.saveAndReturnObject(iTo); return iTo.getId(); } @RolesAllowed("user") @Transactional(readOnly = false) public Long createImage(int sizeX, int sizeY, int sizeZ, int sizeT, List<Integer> channelList, PixelsType pixelsType, String name, String description) { Image image = new Image(); Pixels pixels = new Pixels(); image.setName(name); image.setDescription(description); // Check that the channels in the list are valid. if (channelList == null || channelList.size() == 0) { throw new ValidationException("Channel list must be filled."); } // Create basic metadata pixels.setPixelsType(pixelsType); pixels.setSizeX(sizeX); pixels.setSizeY(sizeY); pixels.setSizeZ(sizeZ); pixels.setSizeC(channelList.size()); pixels.setSizeT(sizeT); pixels.setSha1("Pending..."); pixels.setDimensionOrder(getEnumeration(DimensionOrder.class, "XYZCT")); // Create channel data. List<Channel> channels = createChannels(channelList); for(Channel channel : channels) pixels.addChannel(channel); image.addPixels(pixels); // Save and return our newly created Image Id image = iUpdate.saveAndReturnObject(image); return image.getId(); } @RolesAllowed("user") @Transactional(readOnly = false) public void setChannelGlobalMinMax(long pixelsId, int channelIndex, double min, double max) { Pixels pixels = retrievePixDescription(pixelsId); Channel channel = pixels.getChannel(channelIndex); StatsInfo stats = channel.getStatsInfo(); if (stats == null) { stats = new StatsInfo(); channel.setStatsInfo(stats); } stats.setGlobalMax(max); stats.setGlobalMin(min); iUpdate.saveAndReturnObject(channel); } @RolesAllowed("user") @Transactional(readOnly = false) public void saveRndSettings(RenderingDef rndSettings) { iUpdate.saveAndReturnObject(rndSettings); } @RolesAllowed("user") public int getBitDepth(PixelsType pixelsType) { return PixelData.getBitDepth(pixelsType.getValue()); } @RolesAllowed("user") public <T extends IObject> T getEnumeration(Class<T> klass, String value) { return iQuery.findByString(klass, "value", value); } @RolesAllowed("user") public <T extends IObject> List<T> getAllEnumerations(Class<T> klass) { return iQuery.findAll(klass, null); } /** * Ensures that a particular dimension value is not out of range (ex. less * than zero). * @param value The value to check. * @param name The name of the value to be used for error reporting. * @throws ValidationException If <code>value</code> is out of range. */ private void outOfBoundsCheck(Integer value, String name) { if (value != null && value < 0) { throw new ValidationException(name + ": " + value + " <= 0"); } } /** * Ensures that a particular dimension value in a list is not out of * range (ex. less than zero). * @param channelList The list of channels to check. * @param name The name of the value to be used for error reporting. * @param pixels the pixels the channel list belongs to. * @throws ValidationException If <code>value</code> is out of range. */ private void channelOutOfBounds(List<Integer> channelList, String name, Pixels pixels) { if(channelList.size() == 0) throw new ValidationException("Channel List is not null but size == 0"); for(int i = 0 ; i < channelList.size() ; i++) { int value = channelList.get(i); if (value < 0 || value >= pixels.sizeOfChannels()) throw new ValidationException(name + ": " + i + " out of bounds"); } } /** * Copy the channel from the pixels to the pixels called to. * @param channel the channel index. * @param from the pixel to copy from. * @param to the pixels to copy to. * @param copyStats Whether or not to copy the {@link StatsInfo} for each * channel. */ private void copyChannel(int channel, Pixels from, Pixels to, boolean copyStats) { Channel cFrom = from.getChannel(channel); Channel cTo = new Channel(); cTo.setLogicalChannel(cFrom.getLogicalChannel()); if (copyStats) { cTo.setStatsInfo(new StatsInfo(cFrom.getStatsInfo().getGlobalMin(), cFrom.getStatsInfo().getGlobalMax())); } to.addChannel(cTo); } /** * Creates new channels to be added to a Pixels set. * @param channelList The list of channel emission wavelengths in * nanometers. * @return See above. */ private List<Channel> createChannels(List<Integer> channelList) { List<Channel> channels = new ArrayList<Channel>(); for (Integer wavelength : channelList) { Channel channel = new Channel(); LogicalChannel lc = new LogicalChannel(); channel.setLogicalChannel(lc); channels.add(channel); } return channels; } }