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 }