001 //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/graphics/BlurImage.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 53115 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
045 package org.deegree.graphics;
046
047 import java.awt.AlphaComposite;
048 import java.awt.Composite;
049 import java.awt.Graphics2D;
050 import java.awt.Polygon;
051 import java.awt.color.ColorSpace;
052 import java.awt.image.BufferedImage;
053 import java.awt.image.ColorConvertOp;
054 import java.awt.image.ConvolveOp;
055 import java.awt.image.Kernel;
056 import java.util.ArrayList;
057 import java.util.List;
058
059 import org.deegree.framework.xml.XMLParsingException;
060 import org.deegree.graphics.transformation.WorldToScreenTransform;
061 import org.deegree.model.spatialschema.Envelope;
062 import org.deegree.model.spatialschema.GMLGeometryAdapter;
063 import org.deegree.model.spatialschema.Geometry;
064 import org.deegree.model.spatialschema.GeometryException;
065 import org.deegree.model.spatialschema.MultiSurface;
066 import org.deegree.model.spatialschema.Position;
067 import org.deegree.model.spatialschema.Ring;
068 import org.deegree.model.spatialschema.Surface;
069
070 /**
071 * Display map surface depending on the security parameters. The rest of the Map Image will be
072 * blurred allowing the user a clear view of only the allowed surface.
073 *
074 * @author <a href="mailto:deshmukh@lat-lon.de">Anup Deshmukh</a>
075 *
076 * @author last edited by: $Author: apoth $
077 *
078 * @version 2.0, $Revision: 9340 $, $Date: 2007-12-27 13:32:12 +0100 (Do, 27 Dez 2007) $
079 *
080 * @since 2.0
081 */
082
083 public class BlurImage {
084
085 /**
086 * Create a new BlurImage instance.
087 */
088 public BlurImage() {
089 }
090
091 /**
092 * Render the surface geometry the user is allowed to see. The geometry must be within the
093 * bounding box of the map image.
094 *
095 * 1. Geometry contains bbox -> no need to blur the image 2. Geometry complety within bbox. 3.
096 * BBOX intersects Geometry a. Returns a MultiSurface b. Returns a Surface 4. BBOX disjunkt
097 * Geometry
098 *
099 * @param image
100 * @param bbox
101 * @param geom
102 * @return BufferedImage
103 * @throws GeometryException
104 * @throws XMLParsingException
105 */
106 public BufferedImage renderUserRealm( BufferedImage image, Envelope bbox, Geometry geom )
107 throws GeometryException, XMLParsingException {
108
109 int blurScale = 8;
110 float alpha = 0.2f;
111
112 // Create output image
113 BufferedImage output = null;
114 if ( image.getType() == BufferedImage.TYPE_INT_RGB ) {
115 System.out.println( "setting rgb" );
116 output = new BufferedImage( image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB );
117 } else {
118 System.out.println( "setting rgba" );
119 output = new BufferedImage( image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB );
120 }
121
122 // Transform the world coordinates to screen coordinates.
123 WorldToScreenTransform wsTransform = new WorldToScreenTransform( bbox.getMin().getX(), bbox.getMin().getY(),
124 bbox.getMax().getX(), bbox.getMax().getY(),
125 image.getMinX(), image.getMinY(),
126 image.getWidth(), image.getHeight() );
127
128 Graphics2D graphics = output.createGraphics();
129 Composite composite = graphics.getComposite();
130 graphics.setComposite( AlphaComposite.getInstance( AlphaComposite.SRC_OVER, alpha ) );
131 // blur image, along with the blur scale.
132 graphics.drawImage( blur( grayScale( image ), blurScale ), null, image.getMinX(), image.getMinY() );
133 graphics.setComposite( composite );
134 try {
135 // convert bbox to geometry.
136 StringBuffer envelope = GMLGeometryAdapter.exportAsBox( bbox );
137 Geometry boundingBOX = GMLGeometryAdapter.wrap( envelope.toString(), null );
138 Geometry intersection = boundingBOX.intersection( geom );
139 if ( intersection instanceof Surface ) {
140 Surface surface = (Surface) intersection;
141 Polygon polygon = retrieveSurfacePolygon( surface, wsTransform );
142 graphics = renderClip( graphics, polygon, image );
143 } else if ( intersection instanceof MultiSurface ) {
144 MultiSurface multiSurface = (MultiSurface) intersection;
145 Surface[] surfaces = multiSurface.getAllSurfaces();
146 for ( int i = 0; i < surfaces.length; i++ ) {
147 Surface surface = surfaces[i];
148 Polygon polygon = retrieveSurfacePolygon( surface, wsTransform );
149 graphics = renderClip( graphics, polygon, image );
150 }
151 }
152 } catch ( GeometryException e ) {
153 throw new GeometryException( "Error creating a geometry from the bounding box. " + e );
154 } catch ( XMLParsingException e ) {
155 throw new XMLParsingException( "Error exporting the bounding box to its " + "string format. " + e );
156 }
157 graphics.dispose();
158 return output;
159 }
160
161 /**
162 * Render the clip image on the output graphics context.
163 *
164 * @param graphics
165 * @param polygon
166 * @param image
167 * @return Graphics2D
168 */
169 private Graphics2D renderClip( Graphics2D graphics, Polygon polygon, BufferedImage image ) {
170
171 // clip the region which the user is allowed to see
172 graphics.setClip( polygon );
173 // draw region
174 graphics.drawImage( image, null, image.getMinX(), image.getMinY() );
175
176 return graphics;
177
178 }
179
180 /**
181 * Retrieve the surface as a java.awt.Polygon. The exterior and interior rings are retrieved and
182 * the coordinates transformed to screen coordinates.
183 *
184 * @param surface
185 * @param wsTransform
186 * @return Polygon
187 */
188 private Polygon retrieveSurfacePolygon( Surface surface, WorldToScreenTransform wsTransform ) {
189
190 Ring exteriorRing = surface.getSurfaceBoundary().getExteriorRing();
191 Position[] exteriorPositions = exteriorRing.getPositions();
192 Ring[] interiorRings = surface.getSurfaceBoundary().getInteriorRings();
193
194 Position[][] interiorPositions;
195 if ( interiorRings != null ) {
196 interiorPositions = new Position[interiorRings.length][];
197 for ( int i = 0; i < interiorPositions.length; i++ ) {
198 interiorPositions[i] = interiorRings[i].getPositions();
199 }
200 } else {
201 interiorPositions = new Position[0][];
202 }
203
204 int[] xArray = getXArray( exteriorPositions, interiorPositions, wsTransform );
205 int[] yArray = getYArray( exteriorPositions, interiorPositions, wsTransform );
206
207 Polygon polygon = new Polygon( xArray, yArray, xArray.length );
208
209 return polygon;
210 }
211
212 /**
213 * Retrieve the array of x-coordinates after transformation to screen coordinates.
214 *
215 * @param exteriorRing
216 * @param interiorRing
217 * @param wsTransform
218 * @return int[]
219 */
220 private int[] getXArray( Position[] exteriorRing, Position[][] interiorRing, WorldToScreenTransform wsTransform ) {
221
222 List<Double> xList = new ArrayList<Double>();
223 for ( int i = 0; i < exteriorRing.length; i++ ) {
224 Position position = exteriorRing[i];
225 xList.add( wsTransform.getDestX( position.getX() ) );
226 }
227 for ( int i = 0; i < interiorRing.length; i++ ) {
228 Position[] positions = interiorRing[i];
229 for ( int j = 0; j < positions.length; j++ ) {
230 Position position = positions[j];
231 xList.add( wsTransform.getDestX( position.getX() ) );
232 }
233 }
234
235 int[] xArray = new int[xList.size()];
236 for ( int i = 0; i < xList.size(); i++ ) {
237 Double tmp = xList.get( i );
238 xArray[i] = tmp.intValue();
239 }
240 return xArray;
241 }
242
243 /**
244 * Retrieve the array of y-coordinates after transformation to screen coordinates.
245 *
246 * @param exteriorRing
247 * @param interiorRing
248 * @param wsTransform
249 * @return int[]
250 */
251 private int[] getYArray( Position[] exteriorRing, Position[][] interiorRing, WorldToScreenTransform wsTransform ) {
252
253 List<Double> yList = new ArrayList<Double>();
254 for ( int i = 0; i < exteriorRing.length; i++ ) {
255 Position position = exteriorRing[i];
256 yList.add( wsTransform.getDestY( position.getY() ) );
257 }
258 for ( int i = 0; i < interiorRing.length; i++ ) {
259 Position[] positions = interiorRing[i];
260 for ( int j = 0; j < positions.length; j++ ) {
261 Position position = positions[j];
262 yList.add( wsTransform.getDestY( position.getY() ) );
263 }
264 }
265
266 int[] yArray = new int[yList.size()];
267 for ( int i = 0; i < yList.size(); i++ ) {
268 Double tmp = yList.get( i );
269 yArray[i] = tmp.intValue();
270 }
271 return yArray;
272 }
273
274 /**
275 * Blur effect carried out on the image. The blur scale defines the intensity of the blur.
276 *
277 * @param image
278 * @param blurScale
279 * @return BufferedImage
280 */
281 private BufferedImage blur( BufferedImage image, int blurScale ) {
282
283 BufferedImage destination = null;
284 if ( image.getType() == BufferedImage.TYPE_INT_RGB ) {
285 destination = new BufferedImage( image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB );
286 } else {
287 destination = new BufferedImage( image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB );
288 }
289
290 float[] data = new float[blurScale * blurScale];
291 float value = 1.0f / ( blurScale * blurScale );
292 for ( int i = 0; i < data.length; i++ ) {
293 data[i] = value;
294 }
295 Kernel kernel = new Kernel( blurScale, blurScale, data );
296 ConvolveOp convolve = new ConvolveOp( kernel, ConvolveOp.EDGE_NO_OP, null );
297 convolve.filter( image, destination );
298
299 return destination;
300 }
301
302 /**
303 * Convert BufferedImage RGB to black and white image
304 *
305 * @param image
306 * @return BufferedImage
307 */
308 private BufferedImage grayScale( BufferedImage image ) {
309
310 BufferedImage destination = null;
311 if ( image.getType() == BufferedImage.TYPE_INT_RGB ) {
312 destination = new BufferedImage( image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_RGB );
313 } else {
314 destination = new BufferedImage( image.getWidth(), image.getHeight(), BufferedImage.TYPE_INT_ARGB );
315 }
316 ColorConvertOp colorConvert = new ColorConvertOp( ColorSpace.getInstance( ColorSpace.CS_GRAY ), null );
317 colorConvert.filter( image, destination );
318
319 return destination;
320 }
321
322 }