001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/graphics/displayelements/HorizontalLabel.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.Stroke;
051    import java.awt.TexturePaint;
052    import java.awt.font.LineMetrics;
053    import java.awt.font.TextLayout;
054    import java.awt.geom.AffineTransform;
055    import java.awt.image.BufferedImage;
056    
057    import org.deegree.framework.log.ILogger;
058    import org.deegree.framework.log.LoggerFactory;
059    import org.deegree.graphics.sld.Fill;
060    import org.deegree.graphics.sld.GraphicFill;
061    import org.deegree.graphics.sld.Halo;
062    import org.deegree.model.feature.Feature;
063    import org.deegree.model.filterencoding.FilterEvaluationException;
064    
065    /**
066     * This is a horizontal label with style information and screen coordinates, ready to be rendered to
067     * the view.
068     * <p>
069     * 
070     * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider</a>
071     * @version $Revision: 9340 $ $Date: 2007-12-27 13:32:12 +0100 (Do, 27 Dez 2007) $
072     */
073    
074    class HorizontalLabel implements Label {
075    
076        private static final ILogger LOG = LoggerFactory.getLogger( HorizontalLabel.class );
077    
078        private String caption;
079    
080        private int[] xpoints = new int[4];
081    
082        private int[] ypoints = new int[4];
083    
084        //width and height of the caption
085        private int w;
086        // width and height of the caption
087        private int h;
088    
089        private Color color;
090    
091        private Font font;
092    
093        private int descent;
094    
095        private int ascent;
096    
097        private Halo halo;
098    
099        private Feature feature;
100    
101        HorizontalLabel( String caption, Font font, Color color, LineMetrics metrics, Feature feature, Halo halo, int x,
102                         int y, int w, int h, double anchorPoint[], double[] displacement ) {
103    
104            this.caption = caption;
105            this.font = font;
106            this.color = color;
107            this.descent = (int) metrics.getDescent();
108            this.ascent = (int) metrics.getAscent();
109            this.feature = feature;
110            this.halo = halo;
111    
112            this.w = w;
113            this.h = h;
114    
115            int dx = (int) ( -anchorPoint[0] * w + displacement[0] + 0.5 );
116            int dy = (int) ( anchorPoint[1] * h - displacement[1] + 0.5 );
117    
118            // vertices of label boundary
119            xpoints[0] = x + dx;
120            ypoints[0] = y + dy;
121            xpoints[1] = x + w + dx;
122            ypoints[1] = y + dy;
123            xpoints[2] = x + w + dx;
124            ypoints[2] = y - h + dy;
125            xpoints[3] = x + dx;
126            ypoints[3] = y - h + dy;
127        }
128    
129        /**
130         * @return the text
131         * 
132         */
133        public String getCaption() {
134            return caption;
135        }
136    
137        public void paintBoundaries( Graphics2D g ) {
138            setColor( g, new Color( 0x888888 ), 0.5 );
139            g.fillPolygon( xpoints, ypoints, xpoints.length );
140            g.setColor( Color.BLACK );
141        }
142    
143        /**
144         * Renders the label (including halo) to the submitted <tt>Graphics2D</tt> context.
145         * <p>
146         * 
147         * @param g
148         *            <tt>Graphics2D</tt> context to be used
149         */
150        public void paint( Graphics2D g ) {
151    
152            // render the halo (only if specified)
153            if ( halo != null ) {
154                try {
155                    paintHalo( g, halo, xpoints [0], ypoints [0] - descent );
156                } catch ( FilterEvaluationException e ) {
157                    e.printStackTrace();
158                }
159            }
160    
161            // render the text
162            setColor( g, color, 1.0 );
163            g.setFont( font );
164            g.drawString( caption, xpoints[0], ypoints[0] - descent );
165        }
166    
167        /**
168         * Renders the label's halo to the submitted <tt>Graphics2D</tt> context.
169         * <p>
170         * 
171         * @param g
172         *            <tt>Graphics2D</tt> context to be used
173         * 
174         * @throws FilterEvaluationException
175         *             if the evaluation of a <tt>ParameterValueType</tt> fails
176         */
177        private void paintHalo( Graphics2D g, Halo halo, int x, int y)
178                                throws FilterEvaluationException {
179    
180            int radius = (int) halo.getRadius( feature );
181    
182            // only draw filled rectangle or circle, if Fill-Element is given
183            Fill fill = halo.getFill();
184    
185            if ( fill != null ) {
186                GraphicFill gFill = fill.getGraphicFill();
187    
188                if ( gFill != null ) {
189                    BufferedImage texture = gFill.getGraphic().getAsImage( feature );
190                    Rectangle anchor = new Rectangle( 0, 0, texture.getWidth(), texture.getHeight() );
191                    g.setPaint( new TexturePaint( texture, anchor ) );
192                } else {
193                    double opacity = fill.getOpacity( feature );
194                    Color color = fill.getFill( feature );
195                    setColor( g, color, opacity );
196                }
197                if ( radius < 1 ) {
198                    g.fillRect( x - 1, y - ascent - 1, w + 2, h + 2 );
199                }
200            } else {
201                g.setColor( Color.white );
202            }
203    
204            if ( radius > 0  ) {            
205                TextLayout layout = new TextLayout( caption, font, g.getFontRenderContext() );
206        
207                BasicStroke stroke = new BasicStroke( radius, BasicStroke.CAP_BUTT, BasicStroke.JOIN_ROUND );
208                Stroke oldStroke = g.getStroke();
209                g.setFont( font );
210                g.setStroke( stroke );
211        
212                AffineTransform transform = g.getTransform();
213                AffineTransform oldTransform = (AffineTransform) transform.clone();
214                transform.translate( xpoints[0], ypoints[0] - descent );
215                g.setTransform( transform );
216                g.draw( layout.getOutline( null ) );
217                g.setStroke( oldStroke );
218                g.setTransform( oldTransform );
219            }
220        }
221    
222        public int getX() {
223            return xpoints[0];
224        }
225    
226        public int getY() {
227            return ypoints[0];
228        }
229    
230        public int getMaxX() {
231            return xpoints[1];
232        }
233    
234        public int getMaxY() {
235            return ypoints[1];
236        }
237    
238        public int getMinX() {
239            return xpoints[3];
240        }
241    
242        public int getMinY() {
243            return ypoints[3];
244        }
245    
246        /**
247         * Determines if the label intersects with another label.
248         * <p>
249         * 
250         * @param that
251         *            label to test
252         * @return true if the labels intersect
253         */
254        public boolean intersects( Label that ) {
255    
256            if ( !( that instanceof HorizontalLabel ) ) {
257                LOG.logInfo( "Intersection test for rotated labels is not implemented yet!" );
258                return false;
259            }
260    
261            // coordinates of this Envelope's BBOX
262            double west1 = getMinX();
263            double south1 = getMinY();
264            double east1 = getMaxX();
265            double north1 = getMaxY();
266    
267            // coordinates of the other Envelope's BBOX
268            double west2 = ( (HorizontalLabel) that ).getMinX();
269            double south2 = ( (HorizontalLabel) that ).getMinY();
270            double east2 = ( (HorizontalLabel) that ).getMaxX();
271            double north2 = ( (HorizontalLabel) that ).getMaxY();
272    
273            // special cases: one box lays completly inside the other one
274            if ( ( west1 <= west2 ) && ( south1 <= south2 ) && ( east1 >= east2 ) && ( north1 >= north2 ) ) {
275                return true;
276            }
277            if ( ( west1 >= west2 ) && ( south1 >= south2 ) && ( east1 <= east2 ) && ( north1 <= north2 ) ) {
278                return true;
279            }
280            // in any other case of intersection, at least one line of the BBOX has
281            // to cross a line of the other BBOX
282            // check western boundary of box 1
283            // "touching" boxes must not intersect
284            if ( ( west1 >= west2 ) && ( west1 < east2 ) ) {
285                if ( ( south1 <= south2 ) && ( north1 > south2 ) ) {
286                    return true;
287                }
288    
289                if ( ( south1 < north2 ) && ( north1 >= north2 ) ) {
290                    return true;
291                }
292            }
293            // check eastern boundary of box 1
294            // "touching" boxes must not intersect
295            if ( ( east1 > west2 ) && ( east1 <= east2 ) ) {
296                if ( ( south1 <= south2 ) && ( north1 > south2 ) ) {
297                    return true;
298                }
299    
300                if ( ( south1 < north2 ) && ( north1 >= north2 ) ) {
301                    return true;
302                }
303            }
304            // check southern boundary of box 1
305            // "touching" boxes must not intersect
306            if ( ( south1 >= south2 ) && ( south1 < north2 ) ) {
307                if ( ( west1 <= west2 ) && ( east1 > west2 ) ) {
308                    return true;
309                }
310    
311                if ( ( west1 < east2 ) && ( east1 >= east2 ) ) {
312                    return true;
313                }
314            }
315            // check northern boundary of box 1
316            // "touching" boxes must not intersect
317            if ( ( north1 > south2 ) && ( north1 <= north2 ) ) {
318                if ( ( west1 <= west2 ) && ( east1 > west2 ) ) {
319                    return true;
320                }
321    
322                if ( ( west1 < east2 ) && ( east1 >= east2 ) ) {
323                    return true;
324                }
325            }
326            return false;
327        }
328    
329        private Graphics2D setColor( Graphics2D g2, Color color, double opacity ) {
330            if ( opacity < 0.999 ) {
331                final int alpha = (int) Math.round( opacity * 255 );
332                final int red = color.getRed();
333                final int green = color.getGreen();
334                final int blue = color.getBlue();
335                color = new Color( red, green, blue, alpha );
336            }
337    
338            g2.setColor( color );
339            return g2;
340        }
341    
342        @Override
343        public String toString() {
344            return caption;
345        }
346    }