001 //$HeadURL: http://svn.wald.intevation.org/svn/deegree/base/trunk/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 (Thu, 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 }