001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/graphics/displayelements/HorizontalLabel.java $ 002 /*---------------- FILE HEADER ------------------------------------------ 003 004 This file is part of deegree. 005 Copyright (C) 2001-2008 by: 006 EXSE, Department of Geography, University of Bonn 007 http://www.giub.uni-bonn.de/deegree/ 008 lat/lon GmbH 009 http://www.lat-lon.de 010 011 This library is free software; you can redistribute it and/or 012 modify it under the terms of the GNU Lesser General Public 013 License as published by the Free Software Foundation; either 014 version 2.1 of the License, or (at your option) any later version. 015 016 This library is distributed in the hope that it will be useful, 017 but WITHOUT ANY WARRANTY; without even the implied warranty of 018 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 019 Lesser General Public License for more details. 020 021 You should have received a copy of the GNU Lesser General Public 022 License along with this library; if not, write to the Free Software 023 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 024 025 Contact: 026 027 Andreas Poth 028 lat/lon GmbH 029 Aennchenstr. 19 030 53115 Bonn 031 Germany 032 E-Mail: poth@lat-lon.de 033 034 Prof. Dr. Klaus Greve 035 Department of Geography 036 University of Bonn 037 Meckenheimer Allee 166 038 53115 Bonn 039 Germany 040 E-Mail: greve@giub.uni-bonn.de 041 042 ---------------------------------------------------------------------------*/ 043 package org.deegree.graphics.displayelements; 044 045 import java.awt.BasicStroke; 046 import java.awt.Color; 047 import java.awt.Font; 048 import java.awt.Graphics2D; 049 import java.awt.Rectangle; 050 import java.awt.Stroke; 051 import java.awt.TexturePaint; 052 import java.awt.font.LineMetrics; 053 import java.awt.font.TextLayout; 054 import java.awt.geom.AffineTransform; 055 import java.awt.image.BufferedImage; 056 057 import org.deegree.framework.log.ILogger; 058 import org.deegree.framework.log.LoggerFactory; 059 import org.deegree.graphics.sld.Fill; 060 import org.deegree.graphics.sld.GraphicFill; 061 import org.deegree.graphics.sld.Halo; 062 import org.deegree.model.feature.Feature; 063 import org.deegree.model.filterencoding.FilterEvaluationException; 064 065 /** 066 * This is a horizontal label with style information and screen coordinates, ready to be rendered to 067 * the view. 068 * <p> 069 * 070 * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider</a> 071 * @version $Revision: 9340 $ $Date: 2007-12-27 13:32:12 +0100 (Do, 27 Dez 2007) $ 072 */ 073 074 class HorizontalLabel implements Label { 075 076 private static final ILogger LOG = LoggerFactory.getLogger( HorizontalLabel.class ); 077 078 private String caption; 079 080 private int[] xpoints = new int[4]; 081 082 private int[] ypoints = new int[4]; 083 084 //width and height of the caption 085 private int w; 086 // width and height of the caption 087 private int h; 088 089 private Color color; 090 091 private Font font; 092 093 private int descent; 094 095 private int ascent; 096 097 private Halo halo; 098 099 private Feature feature; 100 101 HorizontalLabel( String caption, Font font, Color color, LineMetrics metrics, Feature feature, Halo halo, int x, 102 int y, int w, int h, double anchorPoint[], double[] displacement ) { 103 104 this.caption = caption; 105 this.font = font; 106 this.color = color; 107 this.descent = (int) metrics.getDescent(); 108 this.ascent = (int) metrics.getAscent(); 109 this.feature = feature; 110 this.halo = halo; 111 112 this.w = w; 113 this.h = h; 114 115 int dx = (int) ( -anchorPoint[0] * w + displacement[0] + 0.5 ); 116 int dy = (int) ( anchorPoint[1] * h - displacement[1] + 0.5 ); 117 118 // vertices of label boundary 119 xpoints[0] = x + dx; 120 ypoints[0] = y + dy; 121 xpoints[1] = x + w + dx; 122 ypoints[1] = y + dy; 123 xpoints[2] = x + w + dx; 124 ypoints[2] = y - h + dy; 125 xpoints[3] = x + dx; 126 ypoints[3] = y - h + dy; 127 } 128 129 /** 130 * @return the text 131 * 132 */ 133 public String getCaption() { 134 return caption; 135 } 136 137 public void paintBoundaries( Graphics2D g ) { 138 setColor( g, new Color( 0x888888 ), 0.5 ); 139 g.fillPolygon( xpoints, ypoints, xpoints.length ); 140 g.setColor( Color.BLACK ); 141 } 142 143 /** 144 * Renders the label (including halo) to the submitted <tt>Graphics2D</tt> context. 145 * <p> 146 * 147 * @param g 148 * <tt>Graphics2D</tt> context to be used 149 */ 150 public void paint( Graphics2D g ) { 151 152 // render the halo (only if specified) 153 if ( halo != null ) { 154 try { 155 paintHalo( g, halo, xpoints [0], ypoints [0] - descent ); 156 } catch ( FilterEvaluationException e ) { 157 e.printStackTrace(); 158 } 159 } 160 161 // render the text 162 setColor( g, color, 1.0 ); 163 g.setFont( font ); 164 g.drawString( caption, xpoints[0], ypoints[0] - descent ); 165 } 166 167 /** 168 * Renders the label's halo to the submitted <tt>Graphics2D</tt> context. 169 * <p> 170 * 171 * @param g 172 * <tt>Graphics2D</tt> context to be used 173 * 174 * @throws FilterEvaluationException 175 * if the evaluation of a <tt>ParameterValueType</tt> fails 176 */ 177 private void paintHalo( Graphics2D g, Halo halo, int x, int y) 178 throws FilterEvaluationException { 179 180 int radius = (int) halo.getRadius( feature ); 181 182 // only draw filled rectangle or circle, if Fill-Element is given 183 Fill fill = halo.getFill(); 184 185 if ( fill != null ) { 186 GraphicFill gFill = fill.getGraphicFill(); 187 188 if ( gFill != null ) { 189 BufferedImage texture = gFill.getGraphic().getAsImage( feature ); 190 Rectangle anchor = new Rectangle( 0, 0, texture.getWidth(), texture.getHeight() ); 191 g.setPaint( new TexturePaint( texture, anchor ) ); 192 } else { 193 double opacity = fill.getOpacity( feature ); 194 Color color = fill.getFill( feature ); 195 setColor( g, color, opacity ); 196 } 197 if ( radius < 1 ) { 198 g.fillRect( x - 1, y - ascent - 1, w + 2, h + 2 ); 199 } 200 } else { 201 g.setColor( Color.white ); 202 } 203 204 if ( radius > 0 ) { 205 TextLayout layout = new TextLayout( caption, font, g.getFontRenderContext() ); 206 207 BasicStroke stroke = new BasicStroke( radius, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND ); 208 Stroke oldStroke = g.getStroke(); 209 g.setFont( font ); 210 g.setStroke( stroke ); 211 212 AffineTransform transform = g.getTransform(); 213 AffineTransform oldTransform = (AffineTransform) transform.clone(); 214 transform.translate( xpoints[0], ypoints[0] - descent ); 215 g.setTransform( transform ); 216 g.draw( layout.getOutline( null ) ); 217 g.setStroke( oldStroke ); 218 g.setTransform( oldTransform ); 219 } 220 } 221 222 public int getX() { 223 return xpoints[0]; 224 } 225 226 public int getY() { 227 return ypoints[0]; 228 } 229 230 public int getMaxX() { 231 return xpoints[1]; 232 } 233 234 public int getMaxY() { 235 return ypoints[1]; 236 } 237 238 public int getMinX() { 239 return xpoints[3]; 240 } 241 242 public int getMinY() { 243 return ypoints[3]; 244 } 245 246 /** 247 * Determines if the label intersects with another label. 248 * <p> 249 * 250 * @param that 251 * label to test 252 * @return true if the labels intersect 253 */ 254 public boolean intersects( Label that ) { 255 256 if ( !( that instanceof HorizontalLabel ) ) { 257 LOG.logInfo( "Intersection test for rotated labels is not implemented yet!" ); 258 return false; 259 } 260 261 // coordinates of this Envelope's BBOX 262 double west1 = getMinX(); 263 double south1 = getMinY(); 264 double east1 = getMaxX(); 265 double north1 = getMaxY(); 266 267 // coordinates of the other Envelope's BBOX 268 double west2 = ( (HorizontalLabel) that ).getMinX(); 269 double south2 = ( (HorizontalLabel) that ).getMinY(); 270 double east2 = ( (HorizontalLabel) that ).getMaxX(); 271 double north2 = ( (HorizontalLabel) that ).getMaxY(); 272 273 // special cases: one box lays completly inside the other one 274 if ( ( west1 <= west2 ) && ( south1 <= south2 ) && ( east1 >= east2 ) && ( north1 >= north2 ) ) { 275 return true; 276 } 277 if ( ( west1 >= west2 ) && ( south1 >= south2 ) && ( east1 <= east2 ) && ( north1 <= north2 ) ) { 278 return true; 279 } 280 // in any other case of intersection, at least one line of the BBOX has 281 // to cross a line of the other BBOX 282 // check western boundary of box 1 283 // "touching" boxes must not intersect 284 if ( ( west1 >= west2 ) && ( west1 < east2 ) ) { 285 if ( ( south1 <= south2 ) && ( north1 > south2 ) ) { 286 return true; 287 } 288 289 if ( ( south1 < north2 ) && ( north1 >= north2 ) ) { 290 return true; 291 } 292 } 293 // check eastern boundary of box 1 294 // "touching" boxes must not intersect 295 if ( ( east1 > west2 ) && ( east1 <= east2 ) ) { 296 if ( ( south1 <= south2 ) && ( north1 > south2 ) ) { 297 return true; 298 } 299 300 if ( ( south1 < north2 ) && ( north1 >= north2 ) ) { 301 return true; 302 } 303 } 304 // check southern boundary of box 1 305 // "touching" boxes must not intersect 306 if ( ( south1 >= south2 ) && ( south1 < north2 ) ) { 307 if ( ( west1 <= west2 ) && ( east1 > west2 ) ) { 308 return true; 309 } 310 311 if ( ( west1 < east2 ) && ( east1 >= east2 ) ) { 312 return true; 313 } 314 } 315 // check northern boundary of box 1 316 // "touching" boxes must not intersect 317 if ( ( north1 > south2 ) && ( north1 <= north2 ) ) { 318 if ( ( west1 <= west2 ) && ( east1 > west2 ) ) { 319 return true; 320 } 321 322 if ( ( west1 < east2 ) && ( east1 >= east2 ) ) { 323 return true; 324 } 325 } 326 return false; 327 } 328 329 private Graphics2D setColor( Graphics2D g2, Color color, double opacity ) { 330 if ( opacity < 0.999 ) { 331 final int alpha = (int) Math.round( opacity * 255 ); 332 final int red = color.getRed(); 333 final int green = color.getGreen(); 334 final int blue = color.getBlue(); 335 color = new Color( red, green, blue, alpha ); 336 } 337 338 g2.setColor( color ); 339 return g2; 340 } 341 342 @Override 343 public String toString() { 344 return caption; 345 } 346 }