001/*
002 *                    BioJava development code
003 *
004 * This code may be freely distributed and modified under the
005 * terms of the GNU Lesser General Public Licence.  This should
006 * be distributed with the code.  If you do not have a copy,
007 * see:
008 *
009 *      http://www.gnu.org/copyleft/lesser.html
010 *
011 * Copyright for this code is held jointly by the individual
012 * authors.  These should be listed in @author doc comments.
013 *
014 * For more information on the BioJava project and its aims,
015 * or to join the biojava-l mailing list, visit the home page
016 * at:
017 *
018 *      http://www.biojava.org/
019 *
020 */
021
022package org.biojava.bio.gui.sequence;
023
024import java.awt.Graphics2D;
025import java.awt.Rectangle;
026import java.awt.event.MouseEvent;
027import java.awt.geom.AffineTransform;
028import java.io.Serializable;
029import java.net.URL;
030import java.util.Iterator;
031
032import org.biojava.bio.seq.Feature;
033import org.biojava.bio.seq.FeatureHolder;
034import org.biojava.bio.symbol.Location;
035import org.biojava.utils.net.URLFactory;
036
037/**
038 * <p><code>BasicImapRenderer</code> is a decorator for
039 * <code>BasicFeatureRenderer</code> which adds the ability to create
040 * HTML image map coordinates which correspond to the feature
041 * rendering produced by the <code>BasicFeatureRenderer</code>.
042 *
043 * @author Simon Foote
044 * @since 1.3
045 */
046public class BasicImapRenderer implements ImageMapRenderer, Serializable
047{
048    private BasicFeatureRenderer renderer;
049    private ImageMap imageMap;
050    private URLFactory urlFactory;
051
052    /**
053     * Creates a new <code>BasicImapRenderer</code>.
054     *
055     * @param renderer a <code>BasicFeatureRenderer</code>.
056     * @param imageMap an <code>ImageMap</code>.
057     * @param urlFactory an <code>URLFactory</code> which should be
058     * capable of creating a suitable URL from each
059     * <code>Feature</code> on the <code>Sequence</code> to be
060     * rendered.
061     */
062    public BasicImapRenderer(BasicFeatureRenderer renderer,
063                             ImageMap             imageMap,
064                             URLFactory           urlFactory)
065    {
066        this.renderer   = renderer;
067        this.imageMap   = imageMap;
068        this.urlFactory = urlFactory;
069    }
070
071    /**
072     * <code>getImageMap</code> returns the current image map.
073     *
074     * @return an <code>ImageMap</code>.
075     */
076    public ImageMap getImageMap()
077    {
078        return imageMap;
079    }
080
081    /**
082     * <code>setImageMap</code> sets the current image map.
083     *
084     * @param imageMap an <code>ImageMap</code>.
085     */
086    public void setImageMap(ImageMap imageMap)
087    {
088        this.imageMap = imageMap;
089    }
090
091    /**
092     * <p><code>renderImageMap</code> writes a set of image map
093     * coordinates corresponding to the rectangle sections drawn by
094     * the renderer. All the block regions of the image receive the
095     * same URL. The hotspots created by this method have the rendered
096     * <code>Feature</code> set as their user object.</p>
097     *
098     * <p>This method is called by <code>renderFeature</code> when
099     * a raster image is rendered.</p>
100     *
101     * @param g2 a <code>Graphics2D</code>.
102     * @param f a <code>Feature</code>.
103     * @param context a <code>SequenceRenderContext</code>.
104     */
105    public void renderImageMap(Graphics2D                 g2,
106                               Feature                     f,
107                               SequenceRenderContext context)
108    {
109        Rectangle bounds = g2.getDeviceConfiguration().getBounds();
110
111        // Safe to cast as bounds come from raster
112        int  mapWidth = (int) bounds.getWidth();
113        int mapHeight = (int) bounds.getHeight();
114
115        URL url = urlFactory.createURL(f);
116
117        double depth = renderer.getDepth(context);
118
119        AffineTransform t = g2.getTransform();
120        double transX = t.getTranslateX();
121        double transY = t.getTranslateY();
122
123        int min, max, dif, x1, y1, x2, y2;
124        double posXW, posYN, width, height;
125
126        Location location = f.getLocation();
127        for (Iterator li = location.blockIterator(); li.hasNext();)
128        {
129            Location loc = (Location) li.next();
130
131            min = loc.getMin();
132            max = loc.getMax();
133            dif = max - min;
134
135            if (context.getDirection() == SequenceRenderContext.HORIZONTAL)
136            {
137                posXW  = context.sequenceToGraphics(min);
138                posYN  = 0.0;
139                width  = Math.max(((double) (dif + 1)) * context.getScale(), 1.0);
140                height = depth;
141            }
142            else
143            {
144                posXW  = 0.0;
145                posYN  = context.sequenceToGraphics(min);
146                width  = depth;
147                height = Math.max(((double) (dif + 1)) * context.getScale(), 1.0);
148            }
149
150            // Apply translation and round
151            x1 = (int) Math.floor(posXW + transX);
152            y1 = (int) Math.floor(posYN + transY);
153            x2 = (int) Math.floor(posXW + width + transX);
154            y2 = (int) Math.floor(posYN + height + transY);
155
156            // If the whole rectangle is outside the image then ignore
157            // it
158            if (! (x1 > mapWidth || y1 > mapHeight || x2 < 0 || y2 < 0))
159            {
160                // Constrain to image size
161                x1 = Math.max(x1, 0);
162                y1 = Math.max(y1, 0);
163                x2 = Math.min(x2, mapWidth);
164                y2 = Math.min(y2, mapHeight);
165
166                Integer [] coordinates = new Integer[4];
167                coordinates[0] = new Integer(x1);
168                coordinates[1] = new Integer(y1);
169                coordinates[2] = new Integer(x2);
170                coordinates[3] = new Integer(y2);
171
172                imageMap.addHotSpot(new ImageMap.HotSpot(ImageMap.RECT,
173                                                         url, coordinates, f));
174            }
175        }
176    }
177
178    public void renderFeature(Graphics2D                 g2,
179                              Feature                     f,
180                              SequenceRenderContext context)
181    {
182        renderImageMap(g2, f, context);
183        renderer.renderFeature(g2, f, context);
184    }
185
186    public double getDepth(SequenceRenderContext context)
187    {
188        return renderer.getDepth(context);
189    }
190
191    public FeatureHolder processMouseEvent(FeatureHolder         holder,
192                                           SequenceRenderContext context,
193                                           MouseEvent            mEvent)
194    {
195        return renderer.processMouseEvent(holder, context, mEvent);
196    }
197}