/*---------------- 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 53177 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.Graphics; import java.awt.Graphics2D; import java.awt.Image; import java.awt.geom.AffineTransform; import java.io.Serializable; import java.util.ArrayList; import java.util.Iterator; import org.deegree.framework.log.ILogger; import org.deegree.framework.log.LoggerFactory; import org.deegree.graphics.sld.LineSymbolizer; import org.deegree.graphics.sld.Symbolizer; import org.deegree.graphics.transformation.GeoTransform; import org.deegree.model.feature.Feature; import org.deegree.model.filterencoding.FilterEvaluationException; import org.deegree.model.spatialschema.Curve; import org.deegree.model.spatialschema.Geometry; import org.deegree.model.spatialschema.GeometryFactory; import org.deegree.model.spatialschema.LineString; import org.deegree.model.spatialschema.MultiCurve; import org.deegree.model.spatialschema.Position; import org.deegree.model.spatialschema.Surface; /** * DisplayElement that encapsulates a linestring (<tt>GM_Curve</tt>) or * multi-linestring geometry (<tt>GM_MultiCurve</tt>) and a * <tt>LineStringSymbolizer</tt>. * <p> * It can be rendered using a solid stroke or a graphics stroke. * <p> * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> * @author <a href="mailto:mschneider@lat-lon.de">Markus Schneider</a> * @version $Revision: 1.10 $ $Date: 2006/09/28 13:32:35 $ */ class LineStringDisplayElement extends GeometryDisplayElement implements DisplayElement, Serializable { private static final ILogger LOG = LoggerFactory.getLogger( LineStringDisplayElement.class ); /** Use serialVersionUID for interoperability. */ private final static long serialVersionUID = -4657962592230618248L; /** * Creates a new LineStringDisplayElement_Impl object. * * @param feature * @param geometry */ public LineStringDisplayElement( Feature feature, Curve geometry ) { super( feature, geometry, null ); Symbolizer defaultSymbolizer = new LineSymbolizer(); this.setSymbolizer( defaultSymbolizer ); } /** * Creates a new LineStringDisplayElement object. * * @param feature * @param geometry * @param symbolizer */ public LineStringDisplayElement( Feature feature, Curve geometry, LineSymbolizer symbolizer ) { super( feature, geometry, symbolizer ); } /** * Creates a new LineStringDisplayElement object. * * @param feature * @param geometry */ public LineStringDisplayElement( Feature feature, MultiCurve geometry ) { super( feature, geometry, null ); Symbolizer defaultSymbolizer = new LineSymbolizer(); this.setSymbolizer( defaultSymbolizer ); } /** * Creates a new LineStringDisplayElement object. * * @param feature * @param geometry * @param symbolizer */ public LineStringDisplayElement( Feature feature, MultiCurve geometry, LineSymbolizer symbolizer ) { super( feature, geometry, symbolizer ); } /** * draws a graphics symbol (image) onto a defined position on the line * * @param image * @param g * @param x * @param y * @param rotation */ private void paintImage( Image image, Graphics2D g, int x, int y, double rotation ) { // get the current transform AffineTransform saveAT = g.getTransform(); // translation parameters (rotation) AffineTransform transform = new AffineTransform(); transform.rotate( rotation, x, y ); transform.translate( -image.getWidth( null ), -image.getHeight( null ) / 2.0 ); g.setTransform( transform ); // render the image g.drawImage( image, x, y, null ); // restore original transform g.setTransform( saveAT ); } /** * renders the DisplayElement to the submitted graphic context */ public void paint( Graphics g, GeoTransform projection, double scale ) { if ( geometry == null ) { return; } // a local instance must be used because following statement may // changes the original geometry Geometry geom = geometry; try { Surface sur = GeometryFactory.createSurface( projection.getSourceRect(), null ); geom = sur.intersection( geometry ); } catch ( Exception e ) { // use original geometry } if ( geom == null ) return; ( (ScaledFeature) feature ).setScale( scale ); LineSymbolizer sym = (LineSymbolizer) symbolizer; org.deegree.graphics.sld.Stroke stroke = sym.getStroke(); // no stroke defined -> don't draw anything if ( stroke == null ) { return; } try { if ( stroke.getOpacity( feature ) > 0.001 ) { // do not paint if feature is completly transparent int[][] pos = null; Graphics2D g2 = (Graphics2D) g; if ( geom instanceof Curve ) { pos = calcTargetCoordinates( projection, (Curve) geom ); drawLine( g2, pos, stroke ); } else { MultiCurve mc = (MultiCurve) geom; for ( int i = 0; i < mc.getSize(); i++ ) { pos = calcTargetCoordinates( projection, mc.getCurveAt( i ) ); drawLine( g2, pos, stroke ); } } } } catch ( Exception e ) { LOG.logError( e.getMessage(), e ); } // GraphicStroke label if ( stroke.getGraphicStroke() != null ) { try { Image image = stroke.getGraphicStroke().getGraphic().getAsImage( feature ); CurveWalker walker = new CurveWalker( g.getClipBounds() ); if ( geom instanceof Curve ) { int[][] pos = LabelFactory.calcScreenCoordinates( projection, (Curve) geom ); ArrayList positions = walker.createPositions( pos, image.getWidth( null ) ); Iterator it = positions.iterator(); while ( it.hasNext() ) { double[] label = (double[]) it.next(); int x = (int) ( label[0] + 0.5 ); int y = (int) ( label[1] + 0.5 ); paintImage( image, (Graphics2D) g, x, y, Math.toRadians( label[2] ) ); } } } catch ( Exception e ) { LOG.logError( e.getMessage(), e ); } } } /** * Calculates the screen coordinates of the curve. */ private int[][] calcTargetCoordinates( GeoTransform projection, Curve curve ) throws Exception { LineString lineString = curve.getAsLineString(); int count = lineString.getNumberOfPoints(); int[][] pos = new int[3][]; pos[0] = new int[count]; pos[1] = new int[count]; pos[2] = new int[1]; int k = 0; for ( int i = 0; i < count; i++ ) { Position position = lineString.getPositionAt( i ); double tx = projection.getDestX( position.getX() ); double ty = projection.getDestY( position.getY() ); if ( i > 0 ) { if ( distance( tx, ty, pos[0][k - 1], pos[1][k - 1] ) > 1 ) { pos[0][k] = (int) ( tx + 0.5 ); pos[1][k] = (int) ( ty + 0.5 ); k++; } } else { pos[0][k] = (int) ( tx + 0.5 ); pos[1][k] = (int) ( ty + 0.5 ); k++; } } pos[2][0] = k; return pos; } /** * Renders a curve to the submitted graphic context. * * TODO: Calculate miterlimit. */ private void drawLine( Graphics g, int[][] pos, org.deegree.graphics.sld.Stroke stroke ) throws FilterEvaluationException { // Color & Opacity Graphics2D g2 = (Graphics2D) g; setColor( g2, stroke.getStroke( feature ), stroke.getOpacity( feature ) ); float[] dash = stroke.getDashArray( feature ); // use a simple Stroke if dash == null or its length < 2 // that's faster float width = (float) stroke.getWidth( feature ); int cap = stroke.getLineCap( feature ); int join = stroke.getLineJoin( feature ); BasicStroke bs2 = null; if ( ( dash == null ) || ( dash.length < 2 ) ) { bs2 = new BasicStroke( width, cap, join ); } else { bs2 = new BasicStroke( width, cap, join, 10.0f, dash, stroke.getDashOffset( feature ) ); } g2.setStroke( bs2 ); g2.drawPolyline( pos[0], pos[1], pos[2][0] ); } private double distance( double x1, double y1, double x2, double y2 ) { return Math.sqrt( ( x2 - x1 ) * ( x2 - x1 ) + ( y2 - y1 ) * ( y2 - y1 ) ); } /** * * * @param g2 * @param color * @param opacity * * @return */ private Graphics2D setColor( Graphics2D g2, Color color, double opacity ) { if ( opacity < 0.999 ) { // just use a color having an alpha channel if a significant // level of transparency has been defined 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; } } /* ******************************************************************** Changes to this class. What the people have been up to: $Log: LineStringDisplayElement.java,v $ Revision 1.10 2006/09/28 13:32:35 poth bug fixes - rendering strokes having a GraphicStroke or GraphicFill Revision 1.9 2006/07/29 08:51:12 poth references to deprecated classes removed Revision 1.8 2006/05/22 07:38:11 poth bug fixes according to handling geometries ********************************************************************** */