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