001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/graphics/displayelements/PolygonDisplayElement.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 53177 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 ---------------------------------------------------------------------------*/ 044 package org.deegree.graphics.displayelements; 045 046 import java.awt.BasicStroke; 047 import java.awt.Color; 048 import java.awt.Graphics; 049 import java.awt.Graphics2D; 050 import java.awt.Image; 051 import java.awt.Rectangle; 052 import java.awt.TexturePaint; 053 import java.awt.geom.AffineTransform; 054 import java.awt.geom.GeneralPath; 055 import java.awt.image.BufferedImage; 056 import java.io.Serializable; 057 import java.util.ArrayList; 058 import java.util.Iterator; 059 import java.util.List; 060 061 import org.deegree.framework.log.ILogger; 062 import org.deegree.framework.log.LoggerFactory; 063 import org.deegree.graphics.sld.GraphicFill; 064 import org.deegree.graphics.sld.PolygonSymbolizer; 065 import org.deegree.graphics.sld.Symbolizer; 066 import org.deegree.graphics.transformation.GeoTransform; 067 import org.deegree.model.feature.Feature; 068 import org.deegree.model.filterencoding.FilterEvaluationException; 069 import org.deegree.model.spatialschema.Envelope; 070 import org.deegree.model.spatialschema.Geometry; 071 import org.deegree.model.spatialschema.GeometryFactory; 072 import org.deegree.model.spatialschema.MultiPrimitive; 073 import org.deegree.model.spatialschema.MultiSurface; 074 import org.deegree.model.spatialschema.Position; 075 import org.deegree.model.spatialschema.Primitive; 076 import org.deegree.model.spatialschema.Surface; 077 import org.deegree.model.spatialschema.SurfacePatch; 078 079 /** 080 * {@link DisplayElement} that encapsulates a {@link Surface} or {@link MultiSurface} geometry and a 081 * {@link PolygonSymbolizer}. 082 * 083 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 084 * @author last edited by: $Author: aschmitz $ 085 * 086 * @version $Revision: 12483 $, $Date: 2008-06-23 13:43:29 +0200 (Mo, 23 Jun 2008) $ 087 */ 088 public class PolygonDisplayElement extends GeometryDisplayElement implements DisplayElement, Serializable { 089 090 private static final ILogger LOG = LoggerFactory.getLogger( PolygonDisplayElement.class ); 091 092 /** Use serialVersionUID for interoperability. */ 093 private final static long serialVersionUID = -2980154437699081214L; 094 095 private List<int[][]> pathes = new ArrayList<int[][]>( 1000 ); 096 097 /** 098 * Creates a new PolygonDisplayElement object. 099 * 100 * @param feature 101 * @param geometry 102 */ 103 public PolygonDisplayElement( Feature feature, Surface geometry ) { 104 super( feature, geometry, null ); 105 106 Symbolizer defaultSymbolizer = new PolygonSymbolizer(); 107 this.setSymbolizer( defaultSymbolizer ); 108 } 109 110 /** 111 * Creates a new PolygonDisplayElement object. 112 * 113 * @param feature 114 * @param geometry 115 * @param symbolizer 116 */ 117 public PolygonDisplayElement( Feature feature, Surface geometry, PolygonSymbolizer symbolizer ) { 118 super( feature, geometry, symbolizer ); 119 } 120 121 /** 122 * Creates a new PolygonDisplayElement object. 123 * 124 * @param feature 125 * @param geometry 126 */ 127 public PolygonDisplayElement( Feature feature, MultiSurface geometry ) { 128 super( feature, geometry, null ); 129 130 Symbolizer defaultSymbolizer = new PolygonSymbolizer(); 131 this.setSymbolizer( defaultSymbolizer ); 132 } 133 134 /** 135 * Creates a new PolygonDisplayElement object. 136 * 137 * @param feature 138 * @param geometry 139 * @param symbolizer 140 */ 141 public PolygonDisplayElement( Feature feature, MultiSurface geometry, PolygonSymbolizer symbolizer ) { 142 super( feature, geometry, symbolizer ); 143 } 144 145 /** 146 * renders the DisplayElement to the submitted graphic context 147 * 148 * @param g 149 * @param projection 150 * @param scale 151 */ 152 public void paint( Graphics g, GeoTransform projection, double scale ) { 153 synchronized ( symbolizer ) { 154 if ( feature != null ) { 155 ( (ScaledFeature) feature ).setScale( scale ); 156 } 157 try { 158 // a local instance must be used because following statement may 159 // changes the original geometry 160 Geometry geom = geometry; 161 if ( geom == null ) { 162 LOG.logInfo( "null geometry in " + this.getClass().getName() ); 163 return; 164 } 165 Envelope env = growEnvelope( projection.getSourceRect(), 0.05f ); 166 Surface tmp = GeometryFactory.createSurface( env, geom.getCoordinateSystem() ); 167 168 if ( !geom.intersects( tmp ) ) { 169 return; 170 } 171 geom = geom.intersection( tmp ); 172 173 if ( geom instanceof Surface ) { 174 GeneralPath path = calcPolygonPath( projection, (Surface) geom ); 175 if ( path != null ) { 176 drawPolygon( g, path ); 177 } else { 178 LOG.logWarning( "null path in " + this.getClass().getName() ); 179 } 180 } else { 181 MultiPrimitive msurface = (MultiPrimitive) geom; 182 for ( int i = 0; i < msurface.getSize(); i++ ) { 183 Primitive prim = msurface.getPrimitiveAt( i ); 184 if ( prim instanceof Surface ) { 185 GeneralPath path = calcPolygonPath( projection, (Surface) prim ); 186 if ( path != null ) { 187 drawPolygon( g, path ); 188 } else { 189 LOG.logWarning( "null path in " + this.getClass().getName() ); 190 } 191 } else { 192 System.out.println( prim.getClass().getName() ); 193 } 194 } 195 } 196 } catch ( FilterEvaluationException e ) { 197 LOG.logError( "FilterEvaluationException caught evaluating an Expression!", e ); 198 } catch ( Exception ex ) { 199 LOG.logError( "Exception caught evaluating an Expression!", ex ); 200 } 201 } 202 } 203 204 private double distance( Position p1, Position p2 ) { 205 double x1 = p1.getX(); 206 double y1 = p1.getY(); 207 double x2 = p2.getX(); 208 double y2 = p2.getY(); 209 return Math.sqrt( ( x2 - x1 ) * ( x2 - x1 ) + ( y2 - y1 ) * ( y2 - y1 ) ); 210 } 211 212 private GeneralPath calcPolygonPath( GeoTransform projection, Surface surface ) 213 throws Exception { 214 GeneralPath path = new GeneralPath(); 215 216 SurfacePatch patch = surface.getSurfacePatchAt( 0 ); 217 if ( patch == null ) 218 return null; 219 appendRingToPath( path, patch.getExteriorRing(), projection ); 220 Position[][] inner = patch.getInteriorRings(); 221 if ( inner != null ) { 222 for ( int i = 0; i < inner.length; i++ ) { 223 appendRingToPath( path, inner[i], projection ); 224 } 225 } 226 227 return path; 228 } 229 230 private void appendRingToPath( GeneralPath path, Position[] ring, GeoTransform projection ) { 231 if ( ring.length == 0 ) 232 return; 233 234 int[] x = new int[ring.length]; 235 int[] y = new int[ring.length]; 236 int k = 0; 237 238 Position p = projection.getDestPoint( ring[0] ); 239 Position pp = p; 240 path.moveTo( (float) p.getX(), (float) p.getY() ); 241 for ( int i = 1; i < ring.length; i++ ) { 242 p = projection.getDestPoint( ring[i] ); 243 if ( distance( p, pp ) > 1 ) { 244 path.lineTo( (float) p.getX(), (float) p.getY() ); 245 pp = p; 246 x[k] = (int) p.getX(); 247 y[k++] = (int) p.getY(); 248 } 249 } 250 path.closePath(); 251 int[][] tmp = new int[3][]; 252 tmp[0] = x; 253 tmp[1] = y; 254 tmp[2] = new int[] { k }; 255 pathes.add( tmp ); 256 } 257 258 private void drawPolygon( Graphics g, GeneralPath path ) 259 throws FilterEvaluationException { 260 Graphics2D g2 = (Graphics2D) g; 261 262 PolygonSymbolizer sym = (PolygonSymbolizer) symbolizer; 263 org.deegree.graphics.sld.Fill fill = sym.getFill(); 264 org.deegree.graphics.sld.Stroke stroke = sym.getStroke(); 265 266 if ( fill != null ) { 267 double opacity = fill.getOpacity( feature ); 268 269 // is completly transparent 270 // if not fill polygon 271 if ( opacity > 0.01 ) { 272 Color color = fill.getFill( feature ); 273 int alpha = (int) Math.round( opacity * 255 ); 274 int red = color.getRed(); 275 int green = color.getGreen(); 276 int blue = color.getBlue(); 277 color = new Color( red, green, blue, alpha ); 278 g2.setColor( color ); 279 GraphicFill gFill = fill.getGraphicFill(); 280 281 if ( gFill != null ) { 282 BufferedImage texture = gFill.getGraphic().getAsImage( feature ); 283 if ( texture != null ) { 284 Rectangle anchor = new Rectangle( 0, 0, texture.getWidth( null ), texture.getHeight( null ) ); 285 g2.setPaint( new TexturePaint( texture, anchor ) ); 286 } 287 } 288 try { 289 g2.fill( path ); 290 } catch ( Exception e ) { 291 // why are all exceptions catched here? 292 } 293 } 294 } 295 296 // only stroke outline, if Stroke-Element is given 297 if ( stroke != null ) { 298 if ( stroke.getOpacity( feature ) > 0.001 ) { 299 // do not paint if feature is completly transparent 300 drawLine( g2, path, stroke ); 301 } 302 if ( stroke.getGraphicStroke() != null ) { 303 try { 304 Image image = stroke.getGraphicStroke().getGraphic().getAsImage( feature ); 305 CurveWalker walker = new CurveWalker( g.getClipBounds() ); 306 307 int[][] pos = null; 308 for ( int i = 0; i < pathes.size(); i++ ) { 309 pos = pathes.get( i ); 310 ArrayList<double[]> positions = walker.createPositions( pos, image.getWidth( null ), true ); 311 Iterator<double[]> it = positions.iterator(); 312 while ( it.hasNext() ) { 313 double[] label = it.next(); 314 int x = (int) ( label[0] + 0.5 ); 315 int y = (int) ( label[1] + 0.5 ); 316 paintImage( image, g2, x, y, Math.toRadians( label[2] ) ); 317 } 318 } 319 } catch ( Exception e ) { 320 LOG.logError( e.getMessage(), e ); 321 } 322 } 323 324 } 325 pathes.clear(); 326 } 327 328 /** 329 * Renders a curve to the submitted graphic context. 330 * 331 * TODO: Calculate miterlimit. 332 */ 333 private void drawLine( Graphics g, GeneralPath path, org.deegree.graphics.sld.Stroke stroke ) 334 throws FilterEvaluationException { 335 336 // Color & Opacity 337 Graphics2D g2 = (Graphics2D) g; 338 setColor( g2, stroke.getStroke( feature ), stroke.getOpacity( feature ) ); 339 340 float[] dash = stroke.getDashArray( feature ); 341 342 // use a simple Stroke if dash == null or its length < 2 343 // that's faster 344 float width = (float) stroke.getWidth( feature ); 345 int cap = stroke.getLineCap( feature ); 346 int join = stroke.getLineJoin( feature ); 347 BasicStroke bs2 = null; 348 349 if ( ( dash == null ) || ( dash.length < 2 ) ) { 350 bs2 = new BasicStroke( width, cap, join ); 351 } else { 352 bs2 = new BasicStroke( width, cap, join, 10.0f, dash, stroke.getDashOffset( feature ) ); 353 } 354 355 g2.setStroke( bs2 ); 356 g2.draw( path ); 357 358 } 359 360 /** 361 * 362 * 363 * @param g2 364 * @param color 365 * @param opacity 366 * 367 * @return the graphics object 368 */ 369 private Graphics2D setColor( Graphics2D g2, Color color, double opacity ) { 370 if ( opacity < 0.999 ) { 371 // just use a color having an alpha channel if a significant 372 // level of transparency has been defined 373 final int alpha = (int) Math.round( opacity * 255 ); 374 final int red = color.getRed(); 375 final int green = color.getGreen(); 376 final int blue = color.getBlue(); 377 color = new Color( red, green, blue, alpha ); 378 } 379 380 g2.setColor( color ); 381 return g2; 382 } 383 384 /** 385 * 386 * @param image 387 * @param g 388 * @param x 389 * @param y 390 * @param rotation 391 */ 392 private void paintImage( Image image, Graphics2D g, int x, int y, double rotation ) { 393 394 // get the current transform 395 AffineTransform saveAT = g.getTransform(); 396 397 // translation parameters (rotation) 398 AffineTransform transform = new AffineTransform(); 399 transform.rotate( rotation, x, y ); 400 transform.translate( -image.getWidth( null ), -image.getHeight( null ) / 2.0 ); 401 g.setTransform( transform ); 402 403 // render the image 404 g.drawImage( image, x, y, null ); 405 406 // restore original transform 407 g.setTransform( saveAT ); 408 } 409 }