001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_testing/src/org/deegree/graphics/BlurImage.java $
002    /*----------------------------------------------------------------------------
003     This file is part of deegree, http://deegree.org/
004     Copyright (C) 2001-2009 by:
005       Department of Geography, University of Bonn
006     and
007       lat/lon GmbH
008    
009     This library is free software; you can redistribute it and/or modify it under
010     the terms of the GNU Lesser General Public License as published by the Free
011     Software Foundation; either version 2.1 of the License, or (at your option)
012     any later version.
013     This library is distributed in the hope that it will be useful, but WITHOUT
014     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016     details.
017     You should have received a copy of the GNU Lesser General Public License
018     along with this library; if not, write to the Free Software Foundation, Inc.,
019     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020    
021     Contact information:
022    
023     lat/lon GmbH
024     Aennchenstr. 19, 53177 Bonn
025     Germany
026     http://lat-lon.de/
027    
028     Department of Geography, University of Bonn
029     Prof. Dr. Klaus Greve
030     Postfach 1147, 53001 Bonn
031     Germany
032     http://www.geographie.uni-bonn.de/deegree/
033    
034     e-mail: info@deegree.org
035    ----------------------------------------------------------------------------*/
036    
037    package org.deegree.graphics;
038    
039    import java.awt.AlphaComposite;
040    import java.awt.Composite;
041    import java.awt.Graphics2D;
042    import java.awt.Polygon;
043    import java.awt.color.ColorSpace;
044    import java.awt.image.BufferedImage;
045    import java.awt.image.ColorConvertOp;
046    import java.awt.image.ConvolveOp;
047    import java.awt.image.Kernel;
048    import java.util.ArrayList;
049    import java.util.List;
050    
051    import org.deegree.framework.xml.XMLParsingException;
052    import org.deegree.graphics.transformation.WorldToScreenTransform;
053    import org.deegree.model.spatialschema.Envelope;
054    import org.deegree.model.spatialschema.GMLGeometryAdapter;
055    import org.deegree.model.spatialschema.Geometry;
056    import org.deegree.model.spatialschema.GeometryException;
057    import org.deegree.model.spatialschema.MultiSurface;
058    import org.deegree.model.spatialschema.Position;
059    import org.deegree.model.spatialschema.Ring;
060    import org.deegree.model.spatialschema.Surface;
061    
062    /**
063     * Display map surface depending on the security parameters. The rest of the Map Image will be
064     * blurred allowing the user a clear view of only the allowed surface.
065     *
066     * @author <a href="mailto:deshmukh@lat-lon.de">Anup Deshmukh</a>
067     *
068     * @author last edited by: $Author: mschneider $
069     *
070     * @version 2.0, $Revision: 18195 $, $Date: 2009-06-18 17:55:39 +0200 (Do, 18. Jun 2009) $
071     *
072     * @since 2.0
073     */
074    
075    public class BlurImage {
076    
077    
078        /**
079         * Render the surface geometry the user is allowed to see. The geometry must be within the
080         * bounding box of the map image.
081         *
082         * 1. Geometry contains bbox -> no need to blur the image 2. Geometry complety within bbox. 3.
083         * BBOX intersects Geometry a. Returns a MultiSurface b. Returns a Surface 4. BBOX disjunkt
084         * Geometry
085         *
086         * @param image
087         * @param bbox
088         * @param geom
089         * @return BufferedImage
090         * @throws GeometryException
091         * @throws XMLParsingException
092         */
093        public BufferedImage renderUserRealm( BufferedImage image, Envelope bbox, Geometry geom )
094                                throws GeometryException, XMLParsingException {
095    
096            int blurScale = 8;
097            float alpha = 0.2f;
098    
099            // Create output image
100            BufferedImage output = null;
101            if ( image.getType() == BufferedImage.TYPE_INT_RGB ) {
102                System.out.println( "setting rgb" );
103                output = new BufferedImage( image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB );
104            } else {
105                System.out.println( "setting rgba" );
106                output = new BufferedImage( image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB );
107            }
108    
109            // Transform the world coordinates to screen coordinates.
110            WorldToScreenTransform wsTransform = new WorldToScreenTransform( bbox.getMin().getX(), bbox.getMin().getY(),
111                                                                             bbox.getMax().getX(), bbox.getMax().getY(),
112                                                                             image.getMinX(), image.getMinY(),
113                                                                             image.getWidth(), image.getHeight() );
114    
115            Graphics2D graphics = output.createGraphics();
116            Composite composite = graphics.getComposite();
117            graphics.setComposite( AlphaComposite.getInstance( AlphaComposite.SRC_OVER, alpha ) );
118            // blur image, along with the blur scale.
119            graphics.drawImage( blur( grayScale( image ), blurScale ), null, image.getMinX(), image.getMinY() );
120            graphics.setComposite( composite );
121            try {
122                // convert bbox to geometry.
123                StringBuffer envelope = GMLGeometryAdapter.exportAsBox( bbox );
124                Geometry boundingBOX = GMLGeometryAdapter.wrap( envelope.toString(), null );
125                Geometry intersection = boundingBOX.intersection( geom );
126                if ( intersection instanceof Surface ) {
127                    Surface surface = (Surface) intersection;
128                    Polygon polygon = retrieveSurfacePolygon( surface, wsTransform );
129                    graphics = renderClip( graphics, polygon, image );
130                } else if ( intersection instanceof MultiSurface ) {
131                    MultiSurface multiSurface = (MultiSurface) intersection;
132                    Surface[] surfaces = multiSurface.getAllSurfaces();
133                    for ( int i = 0; i < surfaces.length; i++ ) {
134                        Surface surface = surfaces[i];
135                        Polygon polygon = retrieveSurfacePolygon( surface, wsTransform );
136                        graphics = renderClip( graphics, polygon, image );
137                    }
138                }
139            } catch ( GeometryException e ) {
140                throw new GeometryException( "Error creating a geometry from the bounding box. " + e );
141            } catch ( XMLParsingException e ) {
142                throw new XMLParsingException( "Error exporting the bounding box to its " + "string format. " + e );
143            }
144            graphics.dispose();
145            return output;
146        }
147    
148        /**
149         * Render the clip image on the output graphics context.
150         *
151         * @param graphics
152         * @param polygon
153         * @param image
154         * @return Graphics2D
155         */
156        private Graphics2D renderClip( Graphics2D graphics, Polygon polygon, BufferedImage image ) {
157    
158            // clip the region which the user is allowed to see
159            graphics.setClip( polygon );
160            // draw region
161            graphics.drawImage( image, null, image.getMinX(), image.getMinY() );
162    
163            return graphics;
164    
165        }
166    
167        /**
168         * Retrieve the surface as a java.awt.Polygon. The exterior and interior rings are retrieved and
169         * the coordinates transformed to screen coordinates.
170         *
171         * @param surface
172         * @param wsTransform
173         * @return Polygon
174         */
175        private Polygon retrieveSurfacePolygon( Surface surface, WorldToScreenTransform wsTransform ) {
176    
177            Ring exteriorRing = surface.getSurfaceBoundary().getExteriorRing();
178            Position[] exteriorPositions = exteriorRing.getPositions();
179            Ring[] interiorRings = surface.getSurfaceBoundary().getInteriorRings();
180    
181            Position[][] interiorPositions;
182            if ( interiorRings != null ) {
183                interiorPositions = new Position[interiorRings.length][];
184                for ( int i = 0; i < interiorPositions.length; i++ ) {
185                    interiorPositions[i] = interiorRings[i].getPositions();
186                }
187            } else {
188                interiorPositions = new Position[0][];
189            }
190    
191            int[] xArray = getXArray( exteriorPositions, interiorPositions, wsTransform );
192            int[] yArray = getYArray( exteriorPositions, interiorPositions, wsTransform );
193    
194            Polygon polygon = new Polygon( xArray, yArray, xArray.length );
195    
196            return polygon;
197        }
198    
199        /**
200         * Retrieve the array of x-coordinates after transformation to screen coordinates.
201         *
202         * @param exteriorRing
203         * @param interiorRing
204         * @param wsTransform
205         * @return int[]
206         */
207        private int[] getXArray( Position[] exteriorRing, Position[][] interiorRing, WorldToScreenTransform wsTransform ) {
208    
209            List<Double> xList = new ArrayList<Double>();
210            for ( int i = 0; i < exteriorRing.length; i++ ) {
211                Position position = exteriorRing[i];
212                xList.add( wsTransform.getDestX( position.getX() ) );
213            }
214            for ( int i = 0; i < interiorRing.length; i++ ) {
215                Position[] positions = interiorRing[i];
216                for ( int j = 0; j < positions.length; j++ ) {
217                    Position position = positions[j];
218                    xList.add( wsTransform.getDestX( position.getX() ) );
219                }
220            }
221    
222            int[] xArray = new int[xList.size()];
223            for ( int i = 0; i < xList.size(); i++ ) {
224                Double tmp = xList.get( i );
225                xArray[i] = tmp.intValue();
226            }
227            return xArray;
228        }
229    
230        /**
231         * Retrieve the array of y-coordinates after transformation to screen coordinates.
232         *
233         * @param exteriorRing
234         * @param interiorRing
235         * @param wsTransform
236         * @return int[]
237         */
238        private int[] getYArray( Position[] exteriorRing, Position[][] interiorRing, WorldToScreenTransform wsTransform ) {
239    
240            List<Double> yList = new ArrayList<Double>();
241            for ( int i = 0; i < exteriorRing.length; i++ ) {
242                Position position = exteriorRing[i];
243                yList.add( wsTransform.getDestY( position.getY() ) );
244            }
245            for ( int i = 0; i < interiorRing.length; i++ ) {
246                Position[] positions = interiorRing[i];
247                for ( int j = 0; j < positions.length; j++ ) {
248                    Position position = positions[j];
249                    yList.add( wsTransform.getDestY( position.getY() ) );
250                }
251            }
252    
253            int[] yArray = new int[yList.size()];
254            for ( int i = 0; i < yList.size(); i++ ) {
255                Double tmp = yList.get( i );
256                yArray[i] = tmp.intValue();
257            }
258            return yArray;
259        }
260    
261        /**
262         * Blur effect carried out on the image. The blur scale defines the intensity of the blur.
263         *
264         * @param image
265         * @param blurScale
266         * @return BufferedImage
267         */
268        private BufferedImage blur( BufferedImage image, int blurScale ) {
269    
270            BufferedImage destination = null;
271            if ( image.getType() == BufferedImage.TYPE_INT_RGB ) {
272                destination = new BufferedImage( image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB );
273            } else {
274                destination = new BufferedImage( image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB );
275            }
276    
277            float[] data = new float[blurScale * blurScale];
278            float value = 1.0f / ( blurScale * blurScale );
279            for ( int i = 0; i < data.length; i++ ) {
280                data[i] = value;
281            }
282            Kernel kernel = new Kernel( blurScale, blurScale, data );
283            ConvolveOp convolve = new ConvolveOp( kernel, ConvolveOp.EDGE_NO_OP, null );
284            convolve.filter( image, destination );
285    
286            return destination;
287        }
288    
289        /**
290         * Convert BufferedImage RGB to black and white image
291         *
292         * @param image
293         * @return BufferedImage
294         */
295        private BufferedImage grayScale( BufferedImage image ) {
296    
297            BufferedImage destination = null;
298            if ( image.getType() == BufferedImage.TYPE_INT_RGB ) {
299                destination = new BufferedImage( image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB );
300            } else {
301                destination = new BufferedImage( image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB );
302            }
303            ColorConvertOp colorConvert = new ColorConvertOp( ColorSpace.getInstance( ColorSpace.CS_GRAY ), null );
304            colorConvert.filter( image, destination );
305    
306            return destination;
307        }
308    
309    }