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