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>ZiggyImapRenderer</code> is a decorator for
039 * <code>ZiggyFeatureRenderer</code> which adds the ability to create
040 * HTML image map coordinates which correspond to the feature
041 * rendering produced by the <code>ZiggyFeatureRenderer</code>. These
042 * coordinates correspond to the block regions of the image rather
043 * than the "elbow" sections which join the locations.</p>
044 *
045 * @author Keith James
046 * @since 1.3
047 */
048public class ZiggyImapRenderer implements ImageMapRenderer, Serializable
049{
050    private ZiggyFeatureRenderer renderer;
051    private ImageMap imageMap;
052    private URLFactory urlFactory;
053
054    /**
055     * Creates a new <code>ZiggyImapRenderer</code>.
056     *
057     * @param renderer a <code>ZiggyFeatureRenderer</code>.
058     * @param imageMap an <code>ImageMap</code>.
059     * @param urlFactory an <code>URLFactory</code> which should be
060     * capable of creating a suitable URL from each
061     * <code>Feature</code> on the <code>Sequence</code> to be
062     * rendered.
063     */
064    public ZiggyImapRenderer(ZiggyFeatureRenderer renderer,
065                             ImageMap             imageMap,
066                             URLFactory           urlFactory)
067    {
068        this.renderer   = renderer;
069        this.imageMap   = imageMap;
070        this.urlFactory = urlFactory;
071    }
072
073    /**
074     * <code>getImageMap</code> returns the current image map.
075     *
076     * @return an <code>ImageMap</code>.
077     */
078    public ImageMap getImageMap()
079    {
080        return imageMap;
081    }
082
083    /**
084     * <code>setImageMap</code> sets the current image map.
085     *
086     * @param imageMap an <code>ImageMap</code>.
087     */
088    public void setImageMap(ImageMap imageMap)
089    {
090        this.imageMap = imageMap;
091    }
092
093    /**
094     * <p><code>renderImageMap</code> writes a set of image map
095     * coordinates corresponding to the rectangle sections drawn by
096     * the renderer. All the block regions of the image receive the
097     * same URL. The hotspots created by this method have the rendered
098     * <code>Feature</code> set as their user object.</p>
099     *
100     * <p>This method is called by <code>renderFeature</code> when
101     * a raster image is rendered.</p>
102     *
103     * @param g2 a <code>Graphics2D</code>.
104     * @param f a <code>Feature</code>.
105     * @param context a <code>SequenceRenderContext</code>.
106     */
107    public void renderImageMap(Graphics2D                 g2,
108                               Feature                     f,
109                               SequenceRenderContext context)
110    {
111        Rectangle bounds = g2.getDeviceConfiguration().getBounds();
112
113        // Safe to cast as bounds come from raster
114        int  mapWidth = (int) bounds.getWidth();
115        int mapHeight = (int) bounds.getHeight();
116
117        URL url = urlFactory.createURL(f);
118
119        double depth = renderer.getBlockDepth();
120
121        AffineTransform t = g2.getTransform();
122        double transX = t.getTranslateX();
123        double transY = t.getTranslateY();
124
125        int min, max, dif, x1, y1, x2, y2;
126        double posXW, posYN, width, height;
127
128        Location location = f.getLocation();
129        for (Iterator li = location.blockIterator(); li.hasNext();)
130        {
131            Location loc = (Location) li.next();
132
133            min = loc.getMin();
134            max = loc.getMax();
135            dif = max - min;
136
137            if (context.getDirection() == SequenceRenderContext.HORIZONTAL)
138            {
139                posXW  = context.sequenceToGraphics(min);
140                posYN  = 0.0;
141                width  = Math.max(((double) (dif + 1)) * context.getScale(), 1.0);
142                height = depth;
143            }
144            else
145            {
146                posXW  = 0.0;
147                posYN  = context.sequenceToGraphics(min);
148                width  = depth;
149                height = Math.max(((double) (dif + 1)) * context.getScale(), 1.0);
150            }
151
152            // Apply translation and round
153            x1 = (int) Math.floor(posXW + transX);
154            y1 = (int) Math.floor(posYN + transY);
155            x2 = (int) Math.floor(posXW + width + transX);
156            y2 = (int) Math.floor(posYN + height + transY);
157
158            // If the whole rectangle is outside the image then ignore
159            // it
160            if (! (x1 > mapWidth || y1 > mapHeight || x2 < 0 || y2 < 0))
161            {
162                // Constrain to image size
163                x1 = Math.max(x1, 0);
164                y1 = Math.max(y1, 0);
165                x2 = Math.min(x2, mapWidth);
166                y2 = Math.min(y2, mapHeight);
167
168                Integer [] coordinates = new Integer[4];
169                coordinates[0] = new Integer(x1);
170                coordinates[1] = new Integer(y1);
171                coordinates[2] = new Integer(x2);
172                coordinates[3] = new Integer(y2);
173
174                imageMap.addHotSpot(new ImageMap.HotSpot(ImageMap.RECT,
175                                                         url, coordinates, f));
176            }
177        }
178    }
179
180    public void renderFeature(Graphics2D                 g2,
181                              Feature                     f,
182                              SequenceRenderContext context)
183    {
184        renderImageMap(g2, f, context);
185        renderer.renderFeature(g2, f, context);
186    }
187
188    public double getDepth(SequenceRenderContext context)
189    {
190        return renderer.getDepth(context);
191    }
192
193    public FeatureHolder processMouseEvent(FeatureHolder         holder,
194                                           SequenceRenderContext context,
195                                           MouseEvent            mEvent)
196    {
197        return renderer.processMouseEvent(holder, context, mEvent);
198    }
199}