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}