001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/graphics/displayelements/RotatedLabel.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.TexturePaint; 051 import java.awt.font.LineMetrics; 052 import java.awt.geom.AffineTransform; 053 import java.awt.image.BufferedImage; 054 055 import org.deegree.framework.log.ILogger; 056 import org.deegree.framework.log.LoggerFactory; 057 import org.deegree.graphics.sld.Fill; 058 import org.deegree.graphics.sld.GraphicFill; 059 import org.deegree.graphics.sld.Halo; 060 import org.deegree.model.feature.Feature; 061 import org.deegree.model.filterencoding.FilterEvaluationException; 062 063 /** 064 * This is a rotated label with style information and screen coordinates, ready to be rendered to 065 * the view. 066 * <p> 067 * 068 * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider</a> 069 * @version $Revision: 9340 $ $Date: 2007-12-27 13:32:12 +0100 (Do, 27 Dez 2007) $ 070 */ 071 072 class RotatedLabel implements Label { 073 074 private static final ILogger LOG = LoggerFactory.getLogger( RotatedLabel.class ); 075 076 private String caption; 077 078 private int[] xpoints; 079 080 private int[] ypoints; 081 082 private double rotation; 083 084 private double anchorPoint[]; 085 086 // width and height of the caption 087 private int w; 088 089 // width and height of the caption 090 private int h; 091 092 private Color color; 093 094 private Font font; 095 096 private int descent; 097 098 private int ascent; 099 100 private Halo halo; 101 102 private Feature feature; 103 104 /** 105 * 106 * @param caption 107 * @param font 108 * @param color 109 * @param metrics 110 * @param feature 111 * @param halo 112 * @param x 113 * @param y 114 * @param w 115 * @param h 116 * @param rotation 117 * @param anchorPoint 118 * @param displacement 119 */ 120 RotatedLabel( String caption, Font font, Color color, LineMetrics metrics, Feature feature, 121 Halo halo, int x, int y, int w, int h, double rotation, double anchorPoint[], 122 double[] displacement ) { 123 124 this.caption = caption; 125 this.font = font; 126 this.color = color; 127 this.descent = (int) metrics.getDescent(); 128 this.ascent = (int) metrics.getAscent(); 129 this.feature = feature; 130 this.halo = halo; 131 this.rotation = rotation; 132 this.anchorPoint = anchorPoint; 133 134 this.w = w; 135 this.h = h; 136 137 // vertices of label boundary 138 int[] xpoints = new int[4]; 139 int[] ypoints = new int[4]; 140 xpoints[0] = x; 141 ypoints[0] = y; 142 xpoints[1] = x + w; 143 ypoints[1] = y; 144 xpoints[2] = x + w; 145 ypoints[2] = y - h; 146 xpoints[3] = x; 147 ypoints[3] = y - h; 148 149 // get rotated + translated points 150 this.xpoints = new int[4]; 151 this.ypoints = new int[4]; 152 int tx = xpoints[0]; 153 int ty = ypoints[0]; 154 155 // transform all vertices of the boundary 156 for ( int i = 0; i < 4; i++ ) { 157 int[] point = transformPoint( xpoints[i], ypoints[i], tx, ty, rotation, 158 displacement[0], displacement[1] ); 159 this.xpoints[i] = point[0]; 160 this.ypoints[i] = point[1]; 161 } 162 } 163 164 /** 165 * 166 * @return caption 167 */ 168 public String getCaption() { 169 return caption; 170 } 171 172 /** 173 * 174 * @return rotation 175 */ 176 public double getRotation() { 177 return rotation; 178 } 179 180 /** 181 * 182 */ 183 public void paintBoundaries( Graphics2D g ) { 184 setColor( g, new Color( 0x888888 ), 0.5 ); 185 g.fillPolygon( xpoints, ypoints, xpoints.length ); 186 g.setColor( Color.BLACK ); 187 188 // get the current transform 189 AffineTransform saveAT = g.getTransform(); 190 191 // translation parameters (rotation) 192 AffineTransform transform = new AffineTransform(); 193 194 // render the text 195 transform.rotate( rotation / 180d * Math.PI, xpoints[0], ypoints[0] ); 196 g.setTransform( transform ); 197 // g.drawString( caption, xpoints [0], ypoints [0] - descent); 198 199 // restore original transform 200 g.setTransform( saveAT ); 201 } 202 203 /** 204 * Renders the label (including halo) to the submitted <tt>Graphics2D</tt> context. 205 * <p> 206 * 207 * @param g 208 * <tt>Graphics2D</tt> context to be used 209 */ 210 public void paint( Graphics2D g ) { 211 212 // get the current transform 213 AffineTransform saveAT = g.getTransform(); 214 215 // perform transformation 216 AffineTransform transform = new AffineTransform(); 217 218 transform.rotate( rotation / 180d * Math.PI, xpoints[0], ypoints[0] ); 219 g.setTransform( transform ); 220 221 // render the halo (only if specified) 222 if ( halo != null ) { 223 try { 224 paintHalo( g, halo, (int) ( xpoints[0] - w * anchorPoint[0] ), 225 (int) ( ypoints[0] - descent + h * anchorPoint[1] ) ); 226 } catch ( FilterEvaluationException e ) { 227 e.printStackTrace(); 228 } 229 } 230 231 // render the text 232 setColor( g, color, 1.0 ); 233 g.setFont( font ); 234 g.drawString( caption, (int) ( xpoints[0] - w * anchorPoint[0] ), 235 (int) ( ypoints[0] - descent + h * anchorPoint[1] ) ); 236 237 // restore original transform 238 g.setTransform( saveAT ); 239 } 240 241 /** 242 * Renders the label's halo to the submitted <tt>Graphics2D</tt> context. 243 * <p> 244 * 245 * @param g 246 * <tt>Graphics2D</tt> context to be used 247 * @param halo 248 * <tt>Halo</tt> from the SLD 249 * @param x 250 * x-coordinate of the label 251 * @param y 252 * y-coordinate of the label 253 * 254 * @throws FilterEvaluationException 255 * if the evaluation of a <tt>ParameterValueType</tt> fails 256 */ 257 private void paintHalo( Graphics2D g, Halo halo, int x, int y ) 258 throws FilterEvaluationException { 259 260 int radius = (int) halo.getRadius( feature ); 261 262 // only draw filled rectangle or circle, if Fill-Element is given 263 Fill fill = halo.getFill(); 264 265 if ( fill != null ) { 266 GraphicFill gFill = fill.getGraphicFill(); 267 268 if ( gFill != null ) { 269 BufferedImage texture = gFill.getGraphic().getAsImage( feature ); 270 Rectangle anchor = new Rectangle( 0, 0, texture.getWidth( null ), 271 texture.getHeight( null ) ); 272 g.setPaint( new TexturePaint( texture, anchor ) ); 273 } else { 274 double opacity = fill.getOpacity( feature ); 275 Color color = fill.getFill( feature ); 276 setColor( g, color, opacity ); 277 } 278 } else { 279 g.setColor( Color.white ); 280 } 281 282 // radius specified -> draw circle 283 if ( radius > 0 ) { 284 g.fillOval( ( x + ( w >> 1 ) ) - radius, y - ( ascent >> 1 ) - radius, radius << 1, 285 radius << 1 ); 286 } 287 // radius unspecified -> draw rectangle 288 else { 289 g.fillRect( x - 1, y - ascent - 1, w + 2, h + 2 ); 290 } 291 292 // only stroke outline, if Stroke-Element is given 293 org.deegree.graphics.sld.Stroke stroke = halo.getStroke(); 294 295 if ( stroke != null ) { 296 double opacity = stroke.getOpacity( feature ); 297 298 if ( opacity > 0.01 ) { 299 Color color = stroke.getStroke( feature ); 300 int alpha = (int) Math.round( opacity * 255 ); 301 int red = color.getRed(); 302 int green = color.getGreen(); 303 int blue = color.getBlue(); 304 color = new Color( red, green, blue, alpha ); 305 g.setColor( color ); 306 307 float[] dash = stroke.getDashArray( feature ); 308 309 // use a simple Stroke if dash == null or dash length < 2 310 BasicStroke bs = null; 311 float strokeWidth = (float) stroke.getWidth( feature ); 312 313 if ( ( dash == null ) || ( dash.length < 2 ) ) { 314 bs = new BasicStroke( strokeWidth ); 315 } else { 316 bs = new BasicStroke( strokeWidth, stroke.getLineCap( feature ), 317 stroke.getLineJoin( feature ), 10.0f, dash, 318 stroke.getDashOffset( feature ) ); 319 bs = new BasicStroke( strokeWidth, stroke.getLineCap( feature ), 320 stroke.getLineJoin( feature ), 1.0f, dash, 1.0f ); 321 } 322 323 g.setStroke( bs ); 324 325 // radius specified -> draw circle 326 if ( radius > 0 ) { 327 g.drawOval( ( x + ( w >> 1 ) ) - radius, y - ( ascent >> 1 ) - radius, 328 radius << 1, radius << 1 ); 329 }// radius unspecified -> draw rectangle 330 else { 331 g.drawRect( x - 1, y - ascent - 1, w + 2, h + 2 ); 332 } 333 } 334 } 335 } 336 337 public int getX() { 338 return xpoints[0]; 339 } 340 341 public int getY() { 342 return ypoints[0]; 343 } 344 345 public int getMaxX() { 346 return xpoints[1]; 347 } 348 349 public int getMaxY() { 350 return ypoints[1]; 351 } 352 353 public int getMinX() { 354 return xpoints[3]; 355 } 356 357 public int getMinY() { 358 return ypoints[3]; 359 } 360 361 /** 362 * Determines if the label intersects with another label. 363 * <p> 364 * 365 * @param that 366 * label to test 367 * @return true if the labels intersect 368 */ 369 public boolean intersects( Label that ) { 370 LOG.logInfo( "Intersection test for rotated labels is not implemented yet!" ); 371 return false; 372 } 373 374 private int[] transformPoint( int x, int y, int tx, int ty, double rotation, 375 double displacementX, double displacementY ) { 376 377 double cos = Math.cos( rotation ); 378 double sin = Math.sin( rotation ); 379 380 double m00 = cos; 381 double m01 = -sin; 382 // double m02 = cos * dx - sin * dy + tx - tx * cos + ty * sin; 383 double m02 = tx - tx * cos + ty * sin; 384 double m10 = sin; 385 double m11 = cos; 386 // double m12 = sin * dx + cos * dy + ty - tx * sin - ty * cos; 387 double m12 = ty - tx * sin - ty * cos; 388 389 int[] point2 = new int[2]; 390 391 point2[0] = (int) ( m00 * x + m01 * y + m02 + 0.5 ); 392 point2[1] = (int) ( m10 * x + m11 * y + m12 + 0.5 ); 393 394 return point2; 395 } 396 397 private Graphics2D setColor( Graphics2D g2, Color color, double opacity ) { 398 if ( opacity < 0.999 ) { 399 final int alpha = (int) Math.round( opacity * 255 ); 400 final int red = color.getRed(); 401 final int green = color.getGreen(); 402 final int blue = color.getBlue(); 403 color = new Color( red, green, blue, alpha ); 404 } 405 406 g2.setColor( color ); 407 return g2; 408 } 409 410 public String toString() { 411 return caption; 412 } 413 }