001 // $HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/model/coverage/grid/AbstractGridCoverage.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 package org.deegree.model.coverage.grid; 045 046 import java.awt.image.BufferedImage; 047 import java.awt.image.DataBuffer; 048 import java.awt.image.Raster; 049 import java.awt.image.renderable.ParameterBlock; 050 import java.io.IOException; 051 import java.io.InputStream; 052 import java.util.Properties; 053 054 import javax.media.jai.InterpolationNearest; 055 import javax.media.jai.JAI; 056 import javax.media.jai.RenderedOp; 057 058 import org.deegree.framework.log.ILogger; 059 import org.deegree.framework.log.LoggerFactory; 060 import org.deegree.framework.util.BootLogger; 061 import org.deegree.graphics.transformation.GeoTransform; 062 import org.deegree.graphics.transformation.WorldToScreenTransform; 063 import org.deegree.model.coverage.AbstractCoverage; 064 import org.deegree.model.coverage.Coverage; 065 import org.deegree.model.crs.CoordinateSystem; 066 import org.deegree.model.spatialschema.Envelope; 067 import org.deegree.ogcwebservices.wcs.describecoverage.CoverageOffering; 068 import org.deegree.processing.raster.converter.Image2RawData; 069 070 /** 071 * Represent the basic implementation which provides access to grid coverage data. A 072 * <code>GC_GridCoverage</code> implementation may provide the ability to update grid values. 073 * 074 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a> 075 * @version 2.11.2002 076 */ 077 078 public abstract class AbstractGridCoverage extends AbstractCoverage implements GridCoverage { 079 080 private static final ILogger LOG = LoggerFactory.getLogger( AbstractGridCoverage.class ); 081 082 private GridGeometry gridGeometry = null; 083 084 private boolean isEditable = false; 085 086 protected static float offset; 087 088 protected static float scaleFactor; 089 090 static { 091 // 16 bit coverage probably does not contain original values but scaled values 092 // with an offset to enable handling of float values. For correct handling of 093 // these coverages offset and scale factor must be known 094 InputStream is = ShortGridCoverage.class.getResourceAsStream( "16bit.properties" ); 095 Properties props = new Properties(); 096 try { 097 props.load( is ); 098 } catch ( IOException e ) { 099 BootLogger.logError( e.getMessage(), e ); 100 } 101 offset = Float.parseFloat( props.getProperty( "offset" ) ); 102 scaleFactor = Float.parseFloat( props.getProperty( "scaleFactor" ) ); 103 } 104 105 /** 106 * @param coverageOffering 107 * @param envelope 108 */ 109 public AbstractGridCoverage( CoverageOffering coverageOffering, Envelope envelope ) { 110 super( coverageOffering, envelope ); 111 } 112 113 /** 114 * @param coverageOffering 115 * @param sources 116 * @param envelope 117 */ 118 public AbstractGridCoverage( CoverageOffering coverageOffering, Envelope envelope, Coverage[] sources ) { 119 super( coverageOffering, envelope, sources ); 120 } 121 122 /** 123 * 124 * @param coverageOffering 125 * @param envelope 126 * @param isEditable 127 */ 128 public AbstractGridCoverage( CoverageOffering coverageOffering, Envelope envelope, boolean isEditable ) { 129 super( coverageOffering, envelope ); 130 this.isEditable = isEditable; 131 } 132 133 /** 134 * 135 * @param coverageOffering 136 * @param envelope 137 * @param isEditable 138 */ 139 public AbstractGridCoverage( CoverageOffering coverageOffering, Envelope envelope, CoordinateSystem crs, 140 boolean isEditable ) { 141 super( coverageOffering, envelope, null, crs ); 142 this.isEditable = isEditable; 143 } 144 145 /** 146 * 147 * @param coverageOffering 148 * @param envelope 149 * @param sources 150 * @param isEditable 151 */ 152 public AbstractGridCoverage( CoverageOffering coverageOffering, Envelope envelope, Coverage[] sources, 153 boolean isEditable ) { 154 super( coverageOffering, envelope, sources ); 155 this.isEditable = isEditable; 156 } 157 158 /** 159 * 160 * @param coverageOffering 161 * @param envelope 162 * @param sources 163 * @param crs 164 * @param isEditable 165 */ 166 public AbstractGridCoverage( CoverageOffering coverageOffering, Envelope envelope, Coverage[] sources, 167 CoordinateSystem crs, boolean isEditable ) { 168 super( coverageOffering, envelope, sources, crs ); 169 this.isEditable = isEditable; 170 } 171 172 /** 173 * Returns <code>true</code> if grid data can be edited. 174 * 175 * @return <code>true</code> if grid data can be edited. 176 */ 177 public boolean isDataEditable() { 178 return isEditable; 179 } 180 181 /** 182 * Information for the grid coverage geometry. Grid geometry includes the valid range of grid 183 * coordinates and the georeferencing. 184 * 185 * @return the information for the grid coverage geometry. 186 * 187 */ 188 public GridGeometry getGridGeometry() { 189 return gridGeometry; 190 } 191 192 /** 193 * this is a deegree convenience method which returns the source image of an 194 * <tt>ImageGridCoverage</tt>. In procipal the same can be done with the 195 * getRenderableImage(int xAxis, int yAxis) method. but creating a <tt>RenderableImage</tt> 196 * image is very slow. 197 * 198 * @param xAxis 199 * Dimension to use for the <var>x</var> axis. 200 * @param yAxis 201 * Dimension to use for the <var>y</var> axis. 202 * @return 203 */ 204 abstract public BufferedImage getAsImage( int xAxis, int yAxis ); 205 206 protected BufferedImage paintImage( BufferedImage targetImg, Envelope targetEnv, BufferedImage sourceImg, 207 Envelope sourceEnv ) { 208 return this.paintImage( targetImg, null, targetEnv, sourceImg, sourceEnv ); 209 } 210 211 /** 212 * renders a source image onto the correct position of a target image according to threir 213 * geographic extends (Envelopes). 214 * 215 * @param targetImg 216 * @param targetEnv 217 * @param sourceImg 218 * @param sourceEnv 219 * @return targetImg with sourceImg rendered on 220 */ 221 protected BufferedImage paintImage( BufferedImage targetImg, float[][] data, Envelope targetEnv, 222 BufferedImage sourceImg, Envelope sourceEnv ) { 223 224 int w = targetImg.getWidth(); 225 int h = targetImg.getHeight(); 226 227 GeoTransform gt = new WorldToScreenTransform( targetEnv.getMin().getX(), targetEnv.getMin().getY(), 228 targetEnv.getMax().getX(), targetEnv.getMax().getY(), 0, 0, 229 w - 1, h - 1 ); 230 int x1 = (int) Math.round( gt.getDestX( sourceEnv.getMin().getX() ) ); 231 int y1 = (int) Math.round( gt.getDestY( sourceEnv.getMax().getY() ) ); 232 int x2 = (int) Math.round( gt.getDestX( sourceEnv.getMax().getX() ) ); 233 int y2 = (int) Math.round( gt.getDestY( sourceEnv.getMin().getY() ) ); 234 235 if ( Math.abs( x2 - x1 ) > 0 && Math.abs( y2 - y1 ) > 0 ) { 236 237 sourceImg = scale( sourceImg, targetImg, sourceEnv, targetEnv ); 238 239 Raster raster = targetImg.getData(); 240 DataBuffer targetBuffer = raster.getDataBuffer(); 241 raster = sourceImg.getData(); 242 DataBuffer srcBuffer = raster.getDataBuffer(); 243 int srcPs = sourceImg.getColorModel().getPixelSize(); 244 int targetPs = targetImg.getColorModel().getPixelSize(); 245 float[][] newData = null; 246 if ( srcPs == 16 && targetPs == 16 ) { 247 Image2RawData i2r = new Image2RawData( sourceImg, 1f / scaleFactor, -1 * offset ); 248 // do not use image api if target bitDepth = 16 249 newData = i2r.parse(); 250 } 251 for ( int i = 0; i < sourceImg.getWidth(); i++ ) { 252 for ( int j = 0; j < sourceImg.getHeight(); j++ ) { 253 if ( x1 + i < targetImg.getWidth() && y1 + j < targetImg.getHeight() ) { 254 int srcPos = sourceImg.getWidth() * j + i; 255 int targetPos = targetImg.getWidth() * ( y1 + j ) + ( i + x1 ); 256 if ( targetPs == 16 && srcPs == 16 ) { 257 // int v = srcBuffer.getElem( srcPos ); 258 // targetBuffer.setElem( targetPos, v ); 259 data[y1 + j][x1 + i] = newData[j][i]; 260 } else if ( targetPs == 16 && srcPs == 32 ) { 261 int v = sourceImg.getRGB( i, j ); 262 float f = Float.intBitsToFloat( v ) * 10f; 263 targetBuffer.setElem( targetPos, Math.round( f ) ); 264 } else if ( targetPs == 32 && srcPs == 16 ) { 265 float f = srcBuffer.getElem( srcPos ) / 10f; 266 targetBuffer.setElem( targetPos, Float.floatToIntBits( f ) ); 267 // TODO 268 // data[y1 + j][x1 + i] = f; 269 } else { 270 targetImg.setRGB( x1 + i, y1 + j, sourceImg.getRGB( i, j ) ); 271 } 272 } 273 } 274 } 275 if ( ( targetPs == 16 && srcPs == 16 ) || ( targetPs == 16 && srcPs == 32 ) 276 || ( targetPs == 32 && srcPs == 16 ) ) { 277 targetImg.setData( Raster.createRaster( targetImg.getSampleModel(), targetBuffer, null ) ); 278 } 279 } 280 281 return targetImg; 282 } 283 284 /** 285 * 286 * @param sourceImg 287 * @param targetImg 288 * @param srcEnv 289 * @param trgEnv 290 * @return 291 */ 292 private BufferedImage scale( BufferedImage sourceImg, BufferedImage targetImg, Envelope srcEnv, Envelope trgEnv ) { 293 294 double srcXres = srcEnv.getWidth() / ( sourceImg.getWidth() - 1f ); 295 double srcYres = srcEnv.getHeight() / ( sourceImg.getHeight() - 1f ); 296 297 double trgXres = trgEnv.getWidth() / ( targetImg.getWidth() - 1f ); 298 double trgYres = trgEnv.getHeight() / ( targetImg.getHeight() - 1f ); 299 300 float sx = (float) ( srcXres / trgXres ); 301 float sy = (float) ( srcYres / trgYres ); 302 303 if ( ( sy < 0.9999 ) || ( sy > 1.0001 ) || ( sx < 0.9999 ) || ( sx > 1.0001 ) ) { 304 try { 305 ParameterBlock pb = new ParameterBlock(); 306 pb.addSource( sourceImg ); 307 308 LOG.logDebug( "Scale image: by factors: " + sx + ' ' + sy ); 309 pb.add( sx ); // The xScale 310 pb.add( sy ); // The yScale 311 pb.add( 0.0F ); // The x translation 312 pb.add( 0.0F ); // The y translation 313 pb.add( new InterpolationNearest() ); // The interpolation 314 // pb.add( new InterpolationBilinear() ); // The interpolation 315 // Create the scale operation 316 RenderedOp ro = JAI.create( "scale", pb, null ); 317 sourceImg = ro.getAsBufferedImage(); 318 } catch ( Exception e ) { 319 LOG.logError( e.getMessage(), e ); 320 } 321 } 322 return sourceImg; 323 } 324 }