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 }