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