001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/tags/2.1/src/org/deegree/graphics/displayelements/HorizontalLabel.java $
002    /*----------------    FILE HEADER  ------------------------------------------
003    
004     This file is part of deegree.
005     Copyright (C) 2001-2006 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: 8211 $ $Date: 2007-09-28 10:43:05 +0200 (Fr, 28 Sep 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    
087        // width and height of the caption
088    //    private int h;
089    
090        private Color color;
091    
092        private Font font;
093    
094        private int descent;
095    
096    //    private int ascent;
097    
098        private Halo halo;
099    
100        private Feature feature;
101    
102        HorizontalLabel( String caption, Font font, Color color, LineMetrics metrics, Feature feature, Halo halo, int x,
103                         int y, int w, int h, double anchorPoint[], double[] displacement ) {
104    
105            this.caption = caption;
106            this.font = font;
107            this.color = color;
108            this.descent = (int) metrics.getDescent();
109    //        this.ascent = (int) metrics.getAscent();
110            this.feature = feature;
111            this.halo = halo;
112    
113    //        this.w = w;
114    //        this.h = h;
115    
116            int dx = (int) ( -anchorPoint[0] * w + displacement[0] + 0.5 );
117            int dy = (int) ( anchorPoint[1] * h - displacement[1] + 0.5 );
118    
119            // vertices of label boundary
120            xpoints[0] = x + dx;
121            ypoints[0] = y + dy;
122            xpoints[1] = x + w + dx;
123            ypoints[1] = y + dy;
124            xpoints[2] = x + w + dx;
125            ypoints[2] = y - h + dy;
126            xpoints[3] = x + dx;
127            ypoints[3] = y - h + dy;
128        }
129    
130        /**
131         * @return the text
132         * 
133         */
134        public String getCaption() {
135            return caption;
136        }
137    
138        public void paintBoundaries( Graphics2D g ) {
139            setColor( g, new Color( 0x888888 ), 0.5 );
140            g.fillPolygon( xpoints, ypoints, xpoints.length );
141            g.setColor( Color.BLACK );
142        }
143    
144        /**
145         * Renders the label (including halo) to the submitted <tt>Graphics2D</tt> context.
146         * <p>
147         * 
148         * @param g
149         *            <tt>Graphics2D</tt> context to be used
150         */
151        public void paint( Graphics2D g ) {
152    
153            // render the halo (only if specified)
154            if ( halo != null ) {
155                try {
156                    paintHalo( g );
157                } catch ( FilterEvaluationException e ) {
158                    e.printStackTrace();
159                }
160            }
161    
162            // render the text
163            setColor( g, color, 1.0 );
164            g.setFont( font );
165            g.drawString( caption, xpoints[0], ypoints[0] - descent );
166        }
167    
168        /**
169         * Renders the label's halo to the submitted <tt>Graphics2D</tt> context.
170         * <p>
171         * 
172         * @param g
173         *            <tt>Graphics2D</tt> context to be used
174         * 
175         * @throws FilterEvaluationException
176         *             if the evaluation of a <tt>ParameterValueType</tt> fails
177         */
178        private void paintHalo( Graphics2D g )
179                                throws FilterEvaluationException {
180    
181            int radius = (int) halo.getRadius( feature );
182    
183            // only draw filled rectangle or circle, if Fill-Element is given
184            Fill fill = halo.getFill();
185    
186            if ( fill != null ) {
187                GraphicFill gFill = fill.getGraphicFill();
188    
189                if ( gFill != null ) {
190                    BufferedImage texture = gFill.getGraphic().getAsImage( feature );
191                    Rectangle anchor = new Rectangle( 0, 0, texture.getWidth(), texture.getHeight() );
192                    g.setPaint( new TexturePaint( texture, anchor ) );
193                } else {
194                    double opacity = fill.getOpacity( feature );
195                    Color color = fill.getFill( feature );
196                    setColor( g, color, opacity );
197                }
198            } else {
199                g.setColor( Color.white );
200            }
201    
202            if ( !( radius > 0 ) ) {
203                radius = 1;
204            }
205    
206            TextLayout layout = new TextLayout( caption, font, g.getFontRenderContext() );
207    
208            BasicStroke stroke = new BasicStroke( radius, BasicStroke.CAP_BUTT, BasicStroke.JOIN_BEVEL );
209            Stroke oldStroke = g.getStroke();
210            g.setFont( font );
211            g.setStroke( stroke );
212    
213            AffineTransform transform = g.getTransform();
214            AffineTransform oldTransform = (AffineTransform) transform.clone();
215            transform.translate( xpoints[0], ypoints[0] - descent );
216            g.setTransform( transform );
217            g.draw( layout.getOutline( null ) );
218            g.setStroke( oldStroke );
219            g.setTransform( oldTransform );
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    }