/*---------------- 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.Rectangle;
import java.awt.TexturePaint;
import java.awt.geom.AffineTransform;
import java.awt.geom.GeneralPath;
import java.awt.image.BufferedImage;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import org.deegree.framework.log.ILogger;
import org.deegree.framework.log.LoggerFactory;
import org.deegree.graphics.sld.GraphicFill;
import org.deegree.graphics.sld.PolygonSymbolizer;
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.Geometry;
import org.deegree.model.spatialschema.MultiPrimitive;
import org.deegree.model.spatialschema.MultiSurface;
import org.deegree.model.spatialschema.Position;
import org.deegree.model.spatialschema.Primitive;
import org.deegree.model.spatialschema.Surface;
import org.deegree.model.spatialschema.SurfacePatch;
/**
* DisplayElement for handling polygons
*
*
* @version $Revision: 1.20 $
* @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
* @author last edited by: $Author: poth $
*
* @version 1.0. $Revision: 1.20 $, $Date: 2006/09/28 13:32:35 $
*
* @since 2.0
*/
public class PolygonDisplayElement extends GeometryDisplayElement implements DisplayElement,
Serializable {
private static final ILogger LOG = LoggerFactory.getLogger( PolygonDisplayElement.class );
/** Use serialVersionUID for interoperability. */
private final static long serialVersionUID = -2980154437699081214L;
private List pathes = new ArrayList( 1000 );
/**
* Creates a new PolygonDisplayElement_Impl object.
*
* @param feature
* @param geometry
*/
public PolygonDisplayElement( Feature feature, Surface geometry ) {
super( feature, geometry, null );
Symbolizer defaultSymbolizer = new PolygonSymbolizer();
this.setSymbolizer( defaultSymbolizer );
}
/**
* Creates a new PolygonDisplayElement_Impl object.
*
* @param feature
* @param geometry
* @param symbolizer
*/
public PolygonDisplayElement( Feature feature, Surface geometry, PolygonSymbolizer symbolizer ) {
super( feature, geometry, symbolizer );
}
/**
* Creates a new PolygonDisplayElement_Impl object.
*
* @param feature
* @param geometry
*/
public PolygonDisplayElement( Feature feature, MultiSurface geometry ) {
super( feature, geometry, null );
Symbolizer defaultSymbolizer = new PolygonSymbolizer();
this.setSymbolizer( defaultSymbolizer );
}
/**
* Creates a new PolygonDisplayElement_Impl object.
*
* @param feature
* @param geometry
* @param symbolizer
*/
public PolygonDisplayElement( Feature feature, MultiSurface geometry,
PolygonSymbolizer symbolizer ) {
super( feature, geometry, symbolizer );
}
/**
* renders the DisplayElement to the submitted graphic context
*/
public void paint( Graphics g, GeoTransform projection, double scale ) {
if ( feature != null ) {
( (ScaledFeature) feature ).setScale( scale );
}
try {
if ( geometry == null ) {
return;
}
// a local instance must be used because following statement may
// changes the original geometry
Geometry geom = geometry;
if ( geom == null ) {
LOG.logInfo( "null geometry in " + this.getClass().getName() );
return;
}
if ( geom instanceof Surface ) {
GeneralPath path = calcPolygonPath( projection, (Surface) geom );
if ( path != null ) {
drawPolygon( g, path );
} else {
LOG.logWarning( "null path in " + this.getClass().getName() );
}
} else {
MultiPrimitive msurface = (MultiPrimitive) geom;
for ( int i = 0; i < msurface.getSize(); i++ ) {
Primitive prim = msurface.getPrimitiveAt( i );
if ( prim instanceof Surface ) {
GeneralPath path = calcPolygonPath( projection, (Surface)prim );
if ( path != null ) {
drawPolygon( g, path );
} else {
LOG.logWarning( "null path in " + this.getClass().getName() );
}
} else {
System.out.println( prim.getClass().getName() );
}
}
}
} catch ( FilterEvaluationException e ) {
LOG.logError( "FilterEvaluationException caught evaluating an Expression!", e );
} catch ( Exception ex ) {
LOG.logError( "Exception caught evaluating an Expression!", ex );
}
}
private double distance( Position p1, Position p2 ) {
double x1 = p1.getX();
double y1 = p1.getY();
double x2 = p2.getX();
double y2 = p2.getY();
return Math.sqrt( ( x2 - x1 ) * ( x2 - x1 ) + ( y2 - y1 ) * ( y2 - y1 ) );
}
private GeneralPath calcPolygonPath(GeoTransform projection, Surface surface )
throws Exception {
GeneralPath path = new GeneralPath();
SurfacePatch patch = surface.getSurfacePatchAt( 0 );
if ( patch == null )
return null;
appendRingToPath(path, patch.getExteriorRing(), projection);
Position[][] inner = patch.getInteriorRings();
if ( inner != null ) {
for (int i = 0; i < inner.length; i++) {
appendRingToPath(path, inner[i], projection);
}
}
return path;
}
private void appendRingToPath(GeneralPath path, Position[] ring,
GeoTransform projection) {
if (ring.length == 0)
return;
int[] x = new int[ring.length];
int[] y = new int[ring.length];
int k = 0;
Position p = projection.getDestPoint( ring[0] );
Position pp = p;
path.moveTo((float)p.getX(), (float)p.getY());
for ( int i = 1; i < ring.length; i++ ) {
p = projection.getDestPoint( ring[i] );
if ( distance( p, pp ) > 1 ) {
path.lineTo((float)p.getX(), (float)p.getY());
pp = p;
x[k] = (int)p.getX();
y[k++] = (int)p.getY();
}
}
int[][] tmp = new int[3][];
tmp[0] = x;
tmp[1] = y;
tmp[2] = new int[] { k };
pathes.add( tmp );
}
private void drawPolygon( Graphics g, GeneralPath path )
throws FilterEvaluationException {
Graphics2D g2 = (Graphics2D) g;
PolygonSymbolizer sym = (PolygonSymbolizer) symbolizer;
org.deegree.graphics.sld.Fill fill = sym.getFill();
org.deegree.graphics.sld.Stroke stroke = sym.getStroke();
if ( fill != null ) {
double opacity = fill.getOpacity( feature );
// is completly transparent
// if not fill polygon
if ( opacity > 0.01 ) {
Color color = fill.getFill( 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 );
g2.setColor( color );
GraphicFill gFill = fill.getGraphicFill();
if ( gFill != null ) {
BufferedImage texture = gFill.getGraphic().getAsImage( feature );
if ( texture != null ) {
Rectangle anchor = new Rectangle( 0, 0, texture.getWidth( null ),
texture.getHeight( null ) );
g2.setPaint( new TexturePaint( texture, anchor ) );
}
}
try {
g2.fill( path );
} catch ( Exception e ) {
}
}
}
// only stroke outline, if Stroke-Element is given
if ( stroke != null ) {
if ( stroke.getOpacity( feature ) > 0.001 ) {
// do not paint if feature is completly transparent
drawLine( g2, path, stroke );
}
if ( stroke.getGraphicStroke() != null ) {
try {
Image image = stroke.getGraphicStroke().getGraphic().getAsImage( feature );
CurveWalker walker = new CurveWalker( g.getClipBounds() );
int[][] pos = null;
for ( int i = 0; i < pathes.size(); i++ ) {
pos = (int[][]) pathes.get( i );
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, g2, x, y, Math.toRadians( label[2] ) );
}
}
} catch ( Exception e ) {
LOG.logError( e.getMessage(), e );
} finally {
pathes.clear();
}
}
}
}
/**
* Renders a curve to the submitted graphic context.
*
* TODO: Calculate miterlimit.
*/
private void drawLine( Graphics g, GeneralPath path, 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.draw( path );
}
/**
*
*
* @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;
}
/**
*
* @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 );
}
}
/* ********************************************************************
Changes to this class. What the people have been up to:
$Log: PolygonDisplayElement.java,v $
Revision 1.20 2006/09/28 13:32:35 poth
bug fixes - rendering strokes having a GraphicStroke or GraphicFill
Revision 1.19 2006/07/04 19:09:06 poth
comments corrected - code formatation
Revision 1.18 2006/07/04 18:30:25 poth
support for MultiPrimitives added
Revision 1.17 2006/06/23 15:18:42 poth
enhanced version using GeneralPath instead of Area to render polygons
Revision 1.16 2006/06/12 06:44:04 poth
*** empty log message ***
Revision 1.15 2006/05/25 16:37:09 poth
bugfix handling outter line coordinates (pathes)
Revision 1.14 2006/05/22 07:38:11 poth
bug fixes according to handling geometries
********************************************************************** */