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 }