001 //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/src/org/deegree/graphics/displayelements/RotatedLabel.java $ 002 /*---------------------------------------------------------------------------- 003 This file is part of deegree, http://deegree.org/ 004 Copyright (C) 2001-2009 by: 005 Department of Geography, University of Bonn 006 and 007 lat/lon GmbH 008 009 This library is free software; you can redistribute it and/or modify it under 010 the terms of the GNU Lesser General Public License as published by the Free 011 Software Foundation; either version 2.1 of the License, or (at your option) 012 any later version. 013 This library is distributed in the hope that it will be useful, but WITHOUT 014 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS 015 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more 016 details. 017 You should have received a copy of the GNU Lesser General Public License 018 along with this library; if not, write to the Free Software Foundation, Inc., 019 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 020 021 Contact information: 022 023 lat/lon GmbH 024 Aennchenstr. 19, 53177 Bonn 025 Germany 026 http://lat-lon.de/ 027 028 Department of Geography, University of Bonn 029 Prof. Dr. Klaus Greve 030 Postfach 1147, 53001 Bonn 031 Germany 032 http://www.geographie.uni-bonn.de/deegree/ 033 034 e-mail: info@deegree.org 035 ----------------------------------------------------------------------------*/ 036 package org.deegree.graphics.displayelements; 037 038 import java.awt.BasicStroke; 039 import java.awt.Color; 040 import java.awt.Font; 041 import java.awt.Graphics2D; 042 import java.awt.Rectangle; 043 import java.awt.TexturePaint; 044 import java.awt.font.LineMetrics; 045 import java.awt.geom.AffineTransform; 046 import java.awt.image.BufferedImage; 047 048 import org.deegree.framework.log.ILogger; 049 import org.deegree.framework.log.LoggerFactory; 050 import org.deegree.graphics.sld.Fill; 051 import org.deegree.graphics.sld.GraphicFill; 052 import org.deegree.graphics.sld.Halo; 053 import org.deegree.model.feature.Feature; 054 import org.deegree.model.filterencoding.FilterEvaluationException; 055 056 /** 057 * This is a rotated label with style information and screen coordinates, ready to be rendered to the view. 058 * <p> 059 * 060 * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider</a> 061 * @version $Revision: 27114 $ $Date: 2010-09-30 16:04:40 +0200 (Do, 30 Sep 2010) $ 062 */ 063 064 class RotatedLabel implements Label { 065 066 private static final ILogger LOG = LoggerFactory.getLogger( RotatedLabel.class ); 067 068 private String caption; 069 070 private int[] xpoints; 071 072 private int[] ypoints; 073 074 private double rotation; 075 076 private double anchorPoint[]; 077 078 // width and height of the caption 079 private int w; 080 081 // width and height of the caption 082 private int h; 083 084 private Color color; 085 086 private Font font; 087 088 private int descent; 089 090 private int ascent; 091 092 private Halo halo; 093 094 private Feature feature; 095 096 private double opacity; 097 098 RotatedLabel( String caption, Font font, Color color, LineMetrics metrics, Feature feature, Halo halo, int x, 099 int y, int w, int h, double rotation, double anchorPoint[], double[] displacement, double opacity ) { 100 this( caption, font, color, metrics, feature, halo, x, y, w, h, rotation, anchorPoint, opacity ); 101 102 int dx = (int) ( -anchorPoint[0] * w + displacement[0] + 0.5 ); 103 int dy = (int) ( anchorPoint[1] * h - displacement[1] + 0.5 ); 104 // vertices of label boundary 105 int[] xpoints = new int[4]; 106 int[] ypoints = new int[4]; 107 xpoints[0] = x + dx; 108 ypoints[0] = y + dy; 109 xpoints[1] = x + w + dx; 110 ypoints[1] = y + dy; 111 xpoints[2] = x + w + dx; 112 ypoints[2] = y - h + dy; 113 xpoints[3] = x + dx; 114 ypoints[3] = y - h + dy; 115 116 // get rotated + translated points 117 this.xpoints = new int[4]; 118 this.ypoints = new int[4]; 119 int tx = xpoints[0]; 120 int ty = ypoints[0]; 121 122 // transform all vertices of the boundary 123 for ( int i = 0; i < 4; i++ ) { 124 int[] point = transformPoint( xpoints[i], ypoints[i], tx, ty, rotation ); 125 this.xpoints[i] = point[0]; 126 this.ypoints[i] = point[1]; 127 } 128 129 this.opacity = opacity; 130 } 131 132 /** 133 * 134 * @param caption 135 * @param font 136 * @param color 137 * @param metrics 138 * @param feature 139 * @param halo 140 * @param x 141 * @param y 142 * @param w 143 * @param h 144 * @param rotation 145 * @param anchorPoint 146 * @param opacity 147 * @Deprecated use 148 * {@link #RotatedLabel(String, Font, Color, LineMetrics, Feature, Halo, int, int, int, int, double, double[], double[], double)} 149 * instead 150 */ 151 RotatedLabel( String caption, Font font, Color color, LineMetrics metrics, Feature feature, Halo halo, int x, 152 int y, int w, int h, double rotation, double anchorPoint[], double opacity ) { 153 this.caption = caption; 154 this.font = font; 155 this.color = color; 156 this.descent = (int) metrics.getDescent(); 157 this.ascent = (int) metrics.getAscent(); 158 this.feature = feature; 159 this.halo = halo; 160 this.rotation = rotation; 161 this.anchorPoint = anchorPoint; 162 163 this.w = w; 164 this.h = h; 165 166 // vertices of label boundary 167 int[] xpoints = new int[4]; 168 int[] ypoints = new int[4]; 169 xpoints[0] = x; 170 ypoints[0] = y; 171 xpoints[1] = x + w; 172 ypoints[1] = y; 173 xpoints[2] = x + w; 174 ypoints[2] = y - h; 175 xpoints[3] = x; 176 ypoints[3] = y - h; 177 178 // get rotated + translated points 179 this.xpoints = new int[4]; 180 this.ypoints = new int[4]; 181 int tx = xpoints[0]; 182 int ty = ypoints[0]; 183 184 // transform all vertices of the boundary 185 for ( int i = 0; i < 4; i++ ) { 186 int[] point = transformPoint( xpoints[i], ypoints[i], tx, ty, rotation ); 187 this.xpoints[i] = point[0]; 188 this.ypoints[i] = point[1]; 189 } 190 191 this.opacity = opacity; 192 } 193 194 /** 195 * 196 * @return caption 197 */ 198 public String getCaption() { 199 return caption; 200 } 201 202 /** 203 * 204 * @return rotation 205 */ 206 public double getRotation() { 207 return rotation; 208 } 209 210 /** 211 * 212 */ 213 public void paintBoundaries( Graphics2D g ) { 214 setColor( g, new Color( 0x888888 ), 0.5 ); 215 g.fillPolygon( xpoints, ypoints, xpoints.length ); 216 g.setColor( Color.BLACK ); 217 218 // get the current transform 219 AffineTransform saveAT = g.getTransform(); 220 221 // translation parameters (rotation) 222 AffineTransform transform = new AffineTransform(); 223 224 // render the text 225 transform.rotate( rotation / 180d * Math.PI, xpoints[0], ypoints[0] ); 226 g.setTransform( transform ); 227 // g.drawString( caption, xpoints [0], ypoints [0] - descent); 228 229 // restore original transform 230 g.setTransform( saveAT ); 231 } 232 233 /** 234 * Renders the label (including halo) to the submitted <tt>Graphics2D</tt> context. 235 * <p> 236 * 237 * @param g 238 * <tt>Graphics2D</tt> context to be used 239 */ 240 public void paint( Graphics2D g ) { 241 242 // get the current transform 243 AffineTransform saveAT = g.getTransform(); 244 245 // perform transformation 246 AffineTransform transform = new AffineTransform(); 247 248 transform.rotate( rotation / 180d * Math.PI, xpoints[0], ypoints[0] ); 249 g.setTransform( transform ); 250 251 // render the halo (only if specified) 252 if ( halo != null ) { 253 try { 254 paintHalo( g, halo, (int) ( xpoints[0] - w * anchorPoint[0] ), 255 (int) ( ypoints[0] - descent + h * anchorPoint[1] ) ); 256 } catch ( FilterEvaluationException e ) { 257 e.printStackTrace(); 258 } 259 } 260 261 // render the text 262 setColor( g, color, opacity ); 263 g.setFont( font ); 264 g.drawString( caption, (int) ( xpoints[0] - w * anchorPoint[0] ), 265 (int) ( ypoints[0] - descent + h * anchorPoint[1] ) ); 266 267 // restore original transform 268 g.setTransform( saveAT ); 269 } 270 271 /** 272 * Renders the label's halo to the submitted <tt>Graphics2D</tt> context. 273 * <p> 274 * 275 * @param g 276 * <tt>Graphics2D</tt> context to be used 277 * @param halo 278 * <tt>Halo</tt> from the SLD 279 * @param x 280 * x-coordinate of the label 281 * @param y 282 * y-coordinate of the label 283 * 284 * @throws FilterEvaluationException 285 * if the evaluation of a <tt>ParameterValueType</tt> fails 286 */ 287 private void paintHalo( Graphics2D g, Halo halo, int x, int y ) 288 throws FilterEvaluationException { 289 290 int radius = (int) halo.getRadius( feature ); 291 292 // only draw filled rectangle or circle, if Fill-Element is given 293 Fill fill = halo.getFill(); 294 295 if ( fill != null ) { 296 GraphicFill gFill = fill.getGraphicFill(); 297 298 if ( gFill != null ) { 299 BufferedImage texture = gFill.getGraphic().getAsImage( feature ); 300 Rectangle anchor = new Rectangle( 0, 0, texture.getWidth( null ), texture.getHeight( null ) ); 301 g.setPaint( new TexturePaint( texture, anchor ) ); 302 } else { 303 double opacity = fill.getOpacity( feature ); 304 Color color = fill.getFill( feature ); 305 setColor( g, color, opacity ); 306 } 307 } else { 308 g.setColor( Color.white ); 309 } 310 311 // radius specified -> draw circle 312 if ( radius > 0 ) { 313 g.fillOval( ( x + ( w >> 1 ) ) - radius, y - ( ascent >> 1 ) - radius, radius << 1, radius << 1 ); 314 } 315 // radius unspecified -> draw rectangle 316 else { 317 g.fillRect( x - 1, y - ascent - 1, w + 2, h + 2 ); 318 } 319 320 // only stroke outline, if Stroke-Element is given 321 org.deegree.graphics.sld.Stroke stroke = halo.getStroke(); 322 323 if ( stroke != null ) { 324 double opacity = stroke.getOpacity( feature ); 325 326 if ( opacity > 0.01 ) { 327 Color color = stroke.getStroke( feature ); 328 int alpha = (int) Math.round( opacity * 255 ); 329 int red = color.getRed(); 330 int green = color.getGreen(); 331 int blue = color.getBlue(); 332 color = new Color( red, green, blue, alpha ); 333 g.setColor( color ); 334 335 float[] dash = stroke.getDashArray( feature ); 336 337 // use a simple Stroke if dash == null or dash length < 2 338 BasicStroke bs = null; 339 float strokeWidth = (float) stroke.getWidth( feature ); 340 341 if ( ( dash == null ) || ( dash.length < 2 ) ) { 342 bs = new BasicStroke( strokeWidth ); 343 } else { 344 bs = new BasicStroke( strokeWidth, stroke.getLineCap( feature ), stroke.getLineJoin( feature ), 345 10.0f, dash, stroke.getDashOffset( feature ) ); 346 bs = new BasicStroke( strokeWidth, stroke.getLineCap( feature ), stroke.getLineJoin( feature ), 347 1.0f, dash, 1.0f ); 348 } 349 350 g.setStroke( bs ); 351 352 // radius specified -> draw circle 353 if ( radius > 0 ) { 354 g.drawOval( ( x + ( w >> 1 ) ) - radius, y - ( ascent >> 1 ) - radius, radius << 1, radius << 1 ); 355 }// radius unspecified -> draw rectangle 356 else { 357 g.drawRect( x - 1, y - ascent - 1, w + 2, h + 2 ); 358 } 359 } 360 } 361 } 362 363 public int getX() { 364 return xpoints[0]; 365 } 366 367 public int getY() { 368 return ypoints[0]; 369 } 370 371 public int getMaxX() { 372 return xpoints[1]; 373 } 374 375 public int getMaxY() { 376 return ypoints[1]; 377 } 378 379 public int getMinX() { 380 return xpoints[3]; 381 } 382 383 public int getMinY() { 384 return ypoints[3]; 385 } 386 387 /** 388 * Determines if the label intersects with another label. 389 * <p> 390 * 391 * @param that 392 * label to test 393 * @return true if the labels intersect 394 */ 395 public boolean intersects( Label that ) { 396 LOG.logInfo( "Intersection test for rotated labels is not implemented yet!" ); 397 return false; 398 } 399 400 private int[] transformPoint( int x, int y, int tx, int ty, double rotation ) { 401 402 double cos = Math.cos( rotation ); 403 double sin = Math.sin( rotation ); 404 405 double m00 = cos; 406 double m01 = -sin; 407 // double m02 = cos * dx - sin * dy + tx - tx * cos + ty * sin; 408 double m02 = tx - tx * cos + ty * sin; 409 double m10 = sin; 410 double m11 = cos; 411 // double m12 = sin * dx + cos * dy + ty - tx * sin - ty * cos; 412 double m12 = ty - tx * sin - ty * cos; 413 414 int[] point2 = new int[2]; 415 416 point2[0] = (int) ( m00 * x + m01 * y + m02 + 0.5 ); 417 point2[1] = (int) ( m10 * x + m11 * y + m12 + 0.5 ); 418 419 return point2; 420 } 421 422 private Graphics2D setColor( Graphics2D g2, Color color, double opacity ) { 423 if ( opacity < 0.999 ) { 424 final int alpha = (int) Math.round( opacity * 255 ); 425 final int red = color.getRed(); 426 final int green = color.getGreen(); 427 final int blue = color.getBlue(); 428 color = new Color( red, green, blue, alpha ); 429 } 430 431 g2.setColor( color ); 432 return g2; 433 } 434 435 @Override 436 public String toString() { 437 return caption; 438 } 439 }