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}