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    }