/*---------------- FILE HEADER ------------------------------------------ This file is part of deegree. Copyright (C) 2001-2006 by: EXSE, Department of Geography, University of Bonn http://www.giub.uni-bonn.de/deegree/ lat/lon GmbH http://www.lat-lon.de This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Contact: Andreas Poth lat/lon GmbH Aennchenstr. 19 53115 Bonn Germany E-Mail: poth@lat-lon.de Prof. Dr. Klaus Greve Department of Geography University of Bonn Meckenheimer Allee 166 53115 Bonn Germany E-Mail: greve@giub.uni-bonn.de ---------------------------------------------------------------------------*/ package org.deegree.graphics.displayelements; import java.awt.BasicStroke; import java.awt.Color; import java.awt.Font; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.TexturePaint; import java.awt.font.LineMetrics; import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import org.deegree.framework.log.ILogger; import org.deegree.framework.log.LoggerFactory; import org.deegree.graphics.sld.Fill; import org.deegree.graphics.sld.GraphicFill; import org.deegree.graphics.sld.Halo; import org.deegree.model.feature.Feature; import org.deegree.model.filterencoding.FilterEvaluationException; /** * This is a rotated label with style information and screen coordinates, * ready to be rendered to the view. * <p> * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider</a> * @version $Revision: 1.9 $ $Date: 2006/08/06 20:16:27 $ */ class RotatedLabel implements Label { private static final ILogger LOG = LoggerFactory.getLogger( RotatedLabel.class ); private String caption; private int [] xpoints; private int [] ypoints; private double rotation; private double anchorPoint []; // width and height of the caption private int w; // width and height of the caption private int h; private Color color; private Font font; private int descent; private int ascent; private Halo halo; private Feature feature; /** * * @param caption * @param font * @param color * @param metrics * @param feature * @param halo * @param x * @param y * @param w * @param h * @param rotation * @param anchorPoint * @param displacement */ RotatedLabel (String caption, Font font, Color color, LineMetrics metrics, Feature feature, Halo halo, int x, int y, int w, int h, double rotation, double anchorPoint [], double [] displacement) { this.caption = caption; this.font = font; this.color = color; this.descent = (int) metrics.getDescent (); this.ascent = (int) metrics.getAscent (); this.feature = feature; this.halo = halo; this.rotation = rotation; this.anchorPoint = anchorPoint; this.w = w; this.h = h; // vertices of label boundary int [] xpoints = new int [4]; int [] ypoints = new int [4]; xpoints [0] = x; ypoints [0] = y; xpoints [1] = x + w; ypoints [1] = y; xpoints [2] = x + w; ypoints [2] = y - h; xpoints [3] = x; ypoints [3] = y - h; // get rotated + translated points this.xpoints = new int [4]; this.ypoints = new int [4]; int tx = xpoints [0]; int ty = ypoints [0]; // transform all vertices of the boundary for (int i = 0; i < 4; i++) { int [] point = transformPoint (xpoints [i], ypoints [i], tx, ty, rotation, displacement [0], displacement [1]); this.xpoints [i] = point [0]; this.ypoints [i] = point [1]; } } /** * * @return */ public String getCaption() { return caption; } /** * * @return */ public double getRotation() { return rotation; } /** * */ public void paintBoundaries (Graphics2D g) { setColor (g, new Color (0x888888), 0.5); g.fillPolygon (xpoints, ypoints, xpoints.length); g.setColor (Color.BLACK); // get the current transform AffineTransform saveAT = g.getTransform(); // translation parameters (rotation) AffineTransform transform = new AffineTransform(); // render the text transform.rotate( rotation/180d*Math.PI, xpoints [0], ypoints [0]); g.setTransform( transform ); //g.drawString( caption, xpoints [0], ypoints [0] - descent); // restore original transform g.setTransform( saveAT ); } /** * Renders the label (including halo) to the submitted * <tt>Graphics2D</tt> context. * <p> * @param g <tt>Graphics2D</tt> context to be used */ public void paint (Graphics2D g) { // get the current transform AffineTransform saveAT = g.getTransform(); // perform transformation AffineTransform transform = new AffineTransform (); transform.rotate( rotation/180d*Math.PI, xpoints [0], ypoints [0]); g.setTransform( transform ); // render the halo (only if specified) if ( halo != null ) { try { paintHalo( g, halo, (int)(xpoints [0]-w*anchorPoint[0]), (int)(ypoints [0] - descent +h*anchorPoint[1]) ); } catch (FilterEvaluationException e) { e.printStackTrace (); } } // render the text setColor( g, color, 1.0 ); g.setFont (font); g.drawString( caption, (int)(xpoints [0]-w*anchorPoint[0]), (int)(ypoints [0] - descent +h*anchorPoint[1]) ); // restore original transform g.setTransform( saveAT ); } /** * Renders the label's halo to the submitted <tt>Graphics2D</tt> context. * <p> * @param g <tt>Graphics2D</tt> context to be used * @param halo <tt>Halo</tt> from the SLD * @param x x-coordinate of the label * @param y y-coordinate of the label * * @throws FilterEvaluationException if the evaluation of a * <tt>ParameterValueType</tt> fails */ private void paintHalo( Graphics2D g, Halo halo, int x, int y) throws FilterEvaluationException { int radius = (int)halo.getRadius( feature ); // only draw filled rectangle or circle, if Fill-Element is given Fill fill = halo.getFill(); if ( fill != null ) { GraphicFill gFill = fill.getGraphicFill(); if ( gFill != null ) { BufferedImage texture = gFill.getGraphic().getAsImage( feature ); Rectangle anchor = new Rectangle( 0, 0, texture.getWidth( null ), texture.getHeight( null ) ); g.setPaint( new TexturePaint( texture, anchor ) ); } else { double opacity = fill.getOpacity( feature ); Color color = fill.getFill( feature ); setColor( g, color, opacity ); } } else { g.setColor( Color.white ); } // radius specified -> draw circle if ( radius > 0 ) { g.fillOval( ( x + ( w >> 1 ) ) - radius, y - ( ascent >> 1 ) - radius, radius << 1, radius << 1 ); } // radius unspecified -> draw rectangle else { g.fillRect( x - 1, y - ascent - 1, w + 2, h + 2 ); } // only stroke outline, if Stroke-Element is given org.deegree.graphics.sld.Stroke stroke = halo.getStroke(); if ( stroke != null ) { double opacity = stroke.getOpacity( feature ); if ( opacity > 0.01 ) { Color color = stroke.getStroke( feature ); int alpha = (int)Math.round( opacity * 255 ); int red = color.getRed(); int green = color.getGreen(); int blue = color.getBlue(); color = new Color( red, green, blue, alpha ); g.setColor( color ); float[] dash = stroke.getDashArray( feature ); // use a simple Stroke if dash == null or dash length < 2 BasicStroke bs = null; float strokeWidth = (float)stroke.getWidth( feature ); if ( ( dash == null ) || ( dash.length < 2 ) ) { bs = new BasicStroke( strokeWidth ); } else { bs = new BasicStroke( strokeWidth, stroke.getLineCap( feature ), stroke.getLineJoin( feature ), 10.0f, dash, stroke.getDashOffset( feature ) ); bs = new BasicStroke( strokeWidth, stroke.getLineCap( feature ), stroke.getLineJoin( feature ), 1.0f, dash, 1.0f ); } g.setStroke( bs ); // radius specified -> draw circle if ( radius > 0 ) { g.drawOval( ( x + ( w >> 1 ) ) - radius, y - ( ascent >> 1 ) - radius, radius << 1, radius << 1 ); }// radius unspecified -> draw rectangle else { g.drawRect( x - 1, y - ascent - 1, w + 2, h + 2 ); } } } } public int getX () { return xpoints [0]; } public int getY () { return ypoints [0]; } public int getMaxX () { return xpoints [1]; } public int getMaxY () { return ypoints [1]; } public int getMinX () { return xpoints [3]; } public int getMinY () { return ypoints [3]; } /** * Determines if the label intersects with another label. * <p> * @param that label to test * @return true if the labels intersect */ public boolean intersects (Label that) { LOG.logInfo( "Intersection test for rotated labels is not implemented yet!"); return false; } private int [] transformPoint (int x, int y, int tx, int ty, double rotation, double displacementX, double displacementY) { double cos = Math.cos (rotation); double sin = Math.sin (rotation); double m00 = cos; double m01 = -sin; //double m02 = cos * dx - sin * dy + tx - tx * cos + ty * sin; double m02 = tx - tx * cos + ty * sin; double m10 = sin; double m11 = cos; //double m12 = sin * dx + cos * dy + ty - tx * sin - ty * cos; double m12 = ty - tx * sin - ty * cos; int [] point2 = new int [2]; point2 [0] = (int) (m00 * x + m01 * y + m02 + 0.5); point2 [1] = (int) (m10 * x + m11 * y + m12 + 0.5); return point2; } private Graphics2D setColor( Graphics2D g2, Color color, double opacity ) { if ( opacity < 0.999 ) { final int alpha = (int)Math.round( opacity * 255 ); final int red = color.getRed(); final int green = color.getGreen(); final int blue = color.getBlue(); color = new Color( red, green, blue, alpha ); } g2.setColor( color ); return g2; } public String toString () { return caption; } } /* ******************************************************************** Changes to this class. What the people have been up to: $Log: RotatedLabel.java,v $ Revision 1.9 2006/08/06 20:16:27 poth never used parameters removed Revision 1.8 2006/07/12 14:46:16 poth comment footer added ********************************************************************** */