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 }