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 }