    001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.4_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: 27112 $ $Date: 2010-09-30 16:04:23 +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    }