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    }