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 }