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 }