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 }