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    }