001 //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_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: 18195 $ $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $ 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 opacity ) { 100 this.caption = caption; 101 this.font = font; 102 this.color = color; 103 this.descent = (int) metrics.getDescent(); 104 this.ascent = (int) metrics.getAscent(); 105 this.feature = feature; 106 this.halo = halo; 107 this.rotation = rotation; 108 this.anchorPoint = anchorPoint; 109 110 this.w = w; 111 this.h = h; 112 113 // vertices of label boundary 114 int[] xpoints = new int[4]; 115 int[] ypoints = new int[4]; 116 xpoints[0] = x; 117 ypoints[0] = y; 118 xpoints[1] = x + w; 119 ypoints[1] = y; 120 xpoints[2] = x + w; 121 ypoints[2] = y - h; 122 xpoints[3] = x; 123 ypoints[3] = y - h; 124 125 // get rotated + translated points 126 this.xpoints = new int[4]; 127 this.ypoints = new int[4]; 128 int tx = xpoints[0]; 129 int ty = ypoints[0]; 130 131 // transform all vertices of the boundary 132 for ( int i = 0; i < 4; i++ ) { 133 int[] point = transformPoint( xpoints[i], ypoints[i], tx, ty, rotation ); 134 this.xpoints[i] = point[0]; 135 this.ypoints[i] = point[1]; 136 } 137 138 this.opacity = opacity; 139 } 140 141 /** 142 * 143 * @return caption 144 */ 145 public String getCaption() { 146 return caption; 147 } 148 149 /** 150 * 151 * @return rotation 152 */ 153 public double getRotation() { 154 return rotation; 155 } 156 157 /** 158 * 159 */ 160 public void paintBoundaries( Graphics2D g ) { 161 setColor( g, new Color( 0x888888 ), 0.5 ); 162 g.fillPolygon( xpoints, ypoints, xpoints.length ); 163 g.setColor( Color.BLACK ); 164 165 // get the current transform 166 AffineTransform saveAT = g.getTransform(); 167 168 // translation parameters (rotation) 169 AffineTransform transform = new AffineTransform(); 170 171 // render the text 172 transform.rotate( rotation / 180d * Math.PI, xpoints[0], ypoints[0] ); 173 g.setTransform( transform ); 174 // g.drawString( caption, xpoints [0], ypoints [0] - descent); 175 176 // restore original transform 177 g.setTransform( saveAT ); 178 } 179 180 /** 181 * Renders the label (including halo) to the submitted <tt>Graphics2D</tt> context. 182 * <p> 183 * 184 * @param g 185 * <tt>Graphics2D</tt> context to be used 186 */ 187 public void paint( Graphics2D g ) { 188 189 // get the current transform 190 AffineTransform saveAT = g.getTransform(); 191 192 // perform transformation 193 AffineTransform transform = new AffineTransform(); 194 195 transform.rotate( rotation / 180d * Math.PI, xpoints[0], ypoints[0] ); 196 g.setTransform( transform ); 197 198 // render the halo (only if specified) 199 if ( halo != null ) { 200 try { 201 paintHalo( g, halo, (int) ( xpoints[0] - w * anchorPoint[0] ), 202 (int) ( ypoints[0] - descent + h * anchorPoint[1] ) ); 203 } catch ( FilterEvaluationException e ) { 204 e.printStackTrace(); 205 } 206 } 207 208 // render the text 209 setColor( g, color, opacity ); 210 g.setFont( font ); 211 g.drawString( caption, (int) ( xpoints[0] - w * anchorPoint[0] ), 212 (int) ( ypoints[0] - descent + h * anchorPoint[1] ) ); 213 214 // restore original transform 215 g.setTransform( saveAT ); 216 } 217 218 /** 219 * Renders the label's halo to the submitted <tt>Graphics2D</tt> context. 220 * <p> 221 * 222 * @param g 223 * <tt>Graphics2D</tt> context to be used 224 * @param halo 225 * <tt>Halo</tt> from the SLD 226 * @param x 227 * x-coordinate of the label 228 * @param y 229 * y-coordinate of the label 230 * 231 * @throws FilterEvaluationException 232 * if the evaluation of a <tt>ParameterValueType</tt> fails 233 */ 234 private void paintHalo( Graphics2D g, Halo halo, int x, int y ) 235 throws FilterEvaluationException { 236 237 int radius = (int) halo.getRadius( feature ); 238 239 // only draw filled rectangle or circle, if Fill-Element is given 240 Fill fill = halo.getFill(); 241 242 if ( fill != null ) { 243 GraphicFill gFill = fill.getGraphicFill(); 244 245 if ( gFill != null ) { 246 BufferedImage texture = gFill.getGraphic().getAsImage( feature ); 247 Rectangle anchor = new Rectangle( 0, 0, texture.getWidth( null ), texture.getHeight( null ) ); 248 g.setPaint( new TexturePaint( texture, anchor ) ); 249 } else { 250 double opacity = fill.getOpacity( feature ); 251 Color color = fill.getFill( feature ); 252 setColor( g, color, opacity ); 253 } 254 } else { 255 g.setColor( Color.white ); 256 } 257 258 // radius specified -> draw circle 259 if ( radius > 0 ) { 260 g.fillOval( ( x + ( w >> 1 ) ) - radius, y - ( ascent >> 1 ) - radius, radius << 1, radius << 1 ); 261 } 262 // radius unspecified -> draw rectangle 263 else { 264 g.fillRect( x - 1, y - ascent - 1, w + 2, h + 2 ); 265 } 266 267 // only stroke outline, if Stroke-Element is given 268 org.deegree.graphics.sld.Stroke stroke = halo.getStroke(); 269 270 if ( stroke != null ) { 271 double opacity = stroke.getOpacity( feature ); 272 273 if ( opacity > 0.01 ) { 274 Color color = stroke.getStroke( feature ); 275 int alpha = (int) Math.round( opacity * 255 ); 276 int red = color.getRed(); 277 int green = color.getGreen(); 278 int blue = color.getBlue(); 279 color = new Color( red, green, blue, alpha ); 280 g.setColor( color ); 281 282 float[] dash = stroke.getDashArray( feature ); 283 284 // use a simple Stroke if dash == null or dash length < 2 285 BasicStroke bs = null; 286 float strokeWidth = (float) stroke.getWidth( feature ); 287 288 if ( ( dash == null ) || ( dash.length < 2 ) ) { 289 bs = new BasicStroke( strokeWidth ); 290 } else { 291 bs = new BasicStroke( strokeWidth, stroke.getLineCap( feature ), stroke.getLineJoin( feature ), 292 10.0f, dash, stroke.getDashOffset( feature ) ); 293 bs = new BasicStroke( strokeWidth, stroke.getLineCap( feature ), stroke.getLineJoin( feature ), 294 1.0f, dash, 1.0f ); 295 } 296 297 g.setStroke( bs ); 298 299 // radius specified -> draw circle 300 if ( radius > 0 ) { 301 g.drawOval( ( x + ( w >> 1 ) ) - radius, y - ( ascent >> 1 ) - radius, radius << 1, radius << 1 ); 302 }// radius unspecified -> draw rectangle 303 else { 304 g.drawRect( x - 1, y - ascent - 1, w + 2, h + 2 ); 305 } 306 } 307 } 308 } 309 310 public int getX() { 311 return xpoints[0]; 312 } 313 314 public int getY() { 315 return ypoints[0]; 316 } 317 318 public int getMaxX() { 319 return xpoints[1]; 320 } 321 322 public int getMaxY() { 323 return ypoints[1]; 324 } 325 326 public int getMinX() { 327 return xpoints[3]; 328 } 329 330 public int getMinY() { 331 return ypoints[3]; 332 } 333 334 /** 335 * Determines if the label intersects with another label. 336 * <p> 337 * 338 * @param that 339 * label to test 340 * @return true if the labels intersect 341 */ 342 public boolean intersects( Label that ) { 343 LOG.logInfo( "Intersection test for rotated labels is not implemented yet!" ); 344 return false; 345 } 346 347 private int[] transformPoint( int x, int y, int tx, int ty, double rotation ) { 348 349 double cos = Math.cos( rotation ); 350 double sin = Math.sin( rotation ); 351 352 double m00 = cos; 353 double m01 = -sin; 354 // double m02 = cos * dx - sin * dy + tx - tx * cos + ty * sin; 355 double m02 = tx - tx * cos + ty * sin; 356 double m10 = sin; 357 double m11 = cos; 358 // double m12 = sin * dx + cos * dy + ty - tx * sin - ty * cos; 359 double m12 = ty - tx * sin - ty * cos; 360 361 int[] point2 = new int[2]; 362 363 point2[0] = (int) ( m00 * x + m01 * y + m02 + 0.5 ); 364 point2[1] = (int) ( m10 * x + m11 * y + m12 + 0.5 ); 365 366 return point2; 367 } 368 369 private Graphics2D setColor( Graphics2D g2, Color color, double opacity ) { 370 if ( opacity < 0.999 ) { 371 final int alpha = (int) Math.round( opacity * 255 ); 372 final int red = color.getRed(); 373 final int green = color.getGreen(); 374 final int blue = color.getBlue(); 375 color = new Color( red, green, blue, alpha ); 376 } 377 378 g2.setColor( color ); 379 return g2; 380 } 381 382 @Override 383 public String toString() { 384 return caption; 385 } 386 }