001    //$HeadURL: svn+ssh://jwilden@svn.wald.intevation.org/deegree/base/branches/2.5_testing/src/org/deegree/io/oraclegeoraster/GeoRasterReader.java $  
002    /*----------------------------------------------------------------------------
003     This file is part of deegree, http://deegree.org/
004     Copyright (C) 2001-2009 by:
005     Department of Geography, University of Bonn
006     and
007     lat/lon GmbH
008     and 
009     grit GmbH   
010     http://www.grit.de
011    
012     This library is free software; you can redistribute it and/or modify it under
013     the terms of the GNU Lesser General Public License as published by the Free
014     Software Foundation; either version 2.1 of the License, or (at your option)
015     any later version.
016     This library is distributed in the hope that it will be useful, but WITHOUT
017     ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
018     FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
019     details.
020     You should have received a copy of the GNU Lesser General Public License
021     along with this library; if not, write to the Free Software Foundation, Inc.,
022     59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
023    
024     Contact information:
025    
026     lat/lon GmbH
027     Aennchenstr. 19, 53177 Bonn
028     Germany
029     http://lat-lon.de/
030    
031     Department of Geography, University of Bonn
032     Prof. Dr. Klaus Greve
033     Postfach 1147, 53001 Bonn
034     Germany
035     http://www.geographie.uni-bonn.de/deegree/
036    
037     e-mail: info@deegree.org
038     ----------------------------------------------------------------------------*/
039    package org.deegree.io.oraclegeoraster;
040    
041    import java.awt.Graphics2D;
042    import java.awt.Image;
043    import java.awt.image.BufferedImage;
044    import java.awt.image.RenderedImage;
045    import java.io.IOException;
046    import java.lang.reflect.Method;
047    import java.sql.Connection;
048    import java.sql.PreparedStatement;
049    import java.sql.ResultSet;
050    import java.sql.SQLException;
051    import java.sql.Statement;
052    import java.util.Properties;
053    
054    import oracle.spatial.georaster.GeoRasterException;
055    import oracle.spatial.georaster.JGeoRaster;
056    import oracle.spatial.georaster.JGeoRasterMeta;
057    import oracle.sql.STRUCT;
058    
059    import org.deegree.framework.log.ILogger;
060    import org.deegree.framework.log.LoggerFactory;
061    import org.deegree.framework.util.StringTools;
062    import org.deegree.graphics.transformation.GeoTransform;
063    import org.deegree.graphics.transformation.WorldToScreenTransform;
064    import org.deegree.io.DBConnectionPool;
065    import org.deegree.io.JDBCConnection;
066    import org.deegree.model.spatialschema.Envelope;
067    import org.deegree.ogcwebservices.InvalidParameterValueException;
068    
069    /**
070     * @version $Revision: 21672 $
071     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
072     * @author <a href="mailto:reichhelm@grit.de">Stephan Reichhelm</a>
073     * @author <a href="mailto:lipski@grit.de">Eryk Lipski</a>
074     * @author last edited by: $Author: apoth $
075     * 
076     *         Currently only tested on Oracle 10gR2.
077     * 
078     * @version 1.0. $Revision: 21672 $, $Date: 2009-12-29 09:44:20 +0100 (Di, 29 Dez 2009) $
079     * 
080     * @since 2.0
081     */
082    public class GeoRasterReader {
083    
084        private static final ILogger LOG = LoggerFactory.getLogger( GeoRasterReader.class );
085    
086        /**
087         * 
088         * @param grDesc
089         * @param envelope
090         *            requested envelope
091         * @param width
092         * @param height
093         * @return rendered image
094         * @throws SQLException
095         * @throws IOException
096         * @throws GeoRasterException
097         * @throws Exception
098         */
099        public static RenderedImage exportRaster( GeoRasterDescription grDesc, Envelope envelope, float width, float height )
100                                throws SQLException, IOException, GeoRasterException, Exception {
101    
102            DBConnectionPool pool = DBConnectionPool.getInstance();
103            JDBCConnection jdbc = grDesc.getJdbcConnection();
104            Connection con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
105    
106            RenderedImage ri = exportRaster( con, envelope, grDesc.getRdtTable(), grDesc.getTable(), grDesc.getColumn(),
107                                             grDesc.getIdentification(), grDesc.getLevel(), width, height );
108    
109            pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
110    
111            return ri;
112        }
113    
114        /**
115         * 
116         * @param connection
117         *            connection to Oracle database
118         * @param envelope
119         *            requested area
120         * @param rasterRDT
121         *            name of the RDT-table
122         * @param rasterTable
123         *            name of the table containing a geo raster column
124         * @param geoRasterCol
125         *            name of the geoRaster column
126         * @param identification
127         *            SQL where clause that identifies the raster of interest
128         * @param level
129         *            requested resolution level
130         * @param width
131         * @param height
132         * @return rendered image
133         * @throws SQLException
134         * @throws IOException
135         * @throws GeoRasterException
136         * @throws Exception
137         */
138        public static RenderedImage exportRaster( Connection connection, Envelope envelope, String rasterRDT,
139                                                  String rasterTable, String geoRasterCol, String identification,
140                                                  int level, float width, float height )
141                                throws Exception {
142            RenderedImage img = null;
143            try {
144    
145                int rasterID = readRasterID( connection, identification, rasterTable, geoRasterCol );
146    
147                STRUCT struct = readGeoRasterMetadata( connection, rasterRDT, rasterTable, geoRasterCol, rasterID );
148    
149                int major = connection.getMetaData().getDriverMajorVersion();
150                int minor = connection.getMetaData().getDriverMinorVersion();
151    
152                JGeoRaster jGeoRaster = null;
153                if ( major == 10 && minor == 1 ) {
154                    // synthax for Oracle 10g R1
155                    Class<?>[] clzz = new Class[] { STRUCT.class };
156                    Method method = JGeoRaster.class.getMethod( "load", clzz );
157                    jGeoRaster = (JGeoRaster) method.invoke( null, new Object[] { struct } );
158                    jGeoRaster = JGeoRaster.load( struct );
159                } else if ( major == 10 && minor == 2 ) {
160                    // synthax for Oracle 10g R2
161                    Class<?>[] clzz = new Class[] { STRUCT.class, Connection.class, boolean.class };
162                    Method method = JGeoRaster.class.getMethod( "load", clzz );
163                    Object[] params = new Object[] { struct, connection, false };
164                    jGeoRaster = (JGeoRaster) method.invoke( null, params );
165                } else {
166                    String s = StringTools.concat( 250, "Oracle driver ", major, ".", minor,
167                                                   " currently not supported for using Georaster functionality. (use 10.1 or 10.2)" );
168                    throw new InvalidParameterValueException( s );
169                }
170                jGeoRaster.setViewerUse( true );
171                Properties props = jGeoRaster.getProperties();
172    
173                int maxWidth = Integer.parseInt( props.getProperty( "rasterInfo/dimensionSize_column" ) );
174                int maxHeight = Integer.parseInt( props.getProperty( "rasterInfo/dimensionSize_row" ) );
175    
176                JGeoRasterMeta metaObj = jGeoRaster.getMetadataObject();
177    
178                // retieve meta-information how row/cells are referenced
179                String metaTyp = "CC";
180                String metaTxt = jGeoRaster.getMetadataString();
181                if ( metaTxt != null
182                     && metaTxt.indexOf( "<modelCoordinateLocation>UPPERLEFT</modelCoordinateLocation>" ) != -1 )
183                    metaTyp = "UL";
184    
185                double xMin = metaObj.getX( 0, 0 );
186                double xMax = metaObj.getX( maxWidth - 1, maxHeight - 1 );
187                double sc = Math.pow( 2, level );
188                double yMin = metaObj.getY( 0, 0 );
189                double yMax = metaObj.getY( maxWidth - 1, maxHeight - 1 );
190    
191                double xDiffPx = Math.abs( ( metaObj.getX( 1, 1 ) - xMin ) / 2 );
192                double yDiffPx = Math.abs( ( metaObj.getY( 1, 1 ) - yMin ) / 2 );
193    
194                // LOG.logDebug(StringTools.concat(350, "georaster-extend ",
195                // xMin, " ", yMin, " - ", xMax, " ", yMax, " typ: ", metaTyp ));
196    
197                /*
198                 * Difference between UL and CC handling
199                 * 
200                 * CC: Extend BBOX from middle-middel-pixel values to outer extend (default) (A -> B)
201                 * 
202                 * B-----* *-----B |\ | | /| | \ | . | / | | A | . | A | | | . | | | | | | *-----* *-----* ... ... *-----*
203                 * *-----* | | | | | | . | | | A | . | A | | / | . | \ | |/ | | \| B-----* *-----B
204                 * 
205                 * UL: Extend BBOX from upper-left-pixel values to outer extend (C=A=B; A->B)
206                 * 
207                 * C-----* A->->-B | | | | | | . | | | | . | | | | . | | | | | | *-----* *-----* ... ... A-----* A-----* | |
208                 * |\ | v | . | \ | | | . | \ | v | . | \ | | | | \| B-----A *-----B
209                 */
210                if ( "UL".equals( metaTyp ) ) {
211                    // koordinatenreferenzierung oben-links
212                    yMax -= ( yDiffPx + yDiffPx );
213                    xMax += ( xDiffPx + xDiffPx );
214                } else {
215                    // koordinatenreferenzierung center-center
216                    xMin -= xDiffPx;
217                    yMin += yDiffPx;
218                    xMax += xDiffPx;
219                    yMax -= yDiffPx;
220                }
221    
222                WorldToScreenTransform wld2ora = new WorldToScreenTransform( xMin, yMin, xMax, yMax, 0, 0, maxWidth - 1,
223                                                                             maxHeight - 1 );
224    
225                int xMinCell = (int) Math.round( wld2ora.getDestX( envelope.getMin().getX() ) / sc );
226                int xMaxCell = (int) Math.round( wld2ora.getDestX( envelope.getMax().getX() ) / sc );
227                int yMaxCell = (int) Math.round( wld2ora.getDestY( envelope.getMin().getY() ) / sc );
228                int yMinCell = (int) Math.round( wld2ora.getDestY( envelope.getMax().getY() ) / sc );
229    
230                if ( LOG.isDebug() ) {
231                    LOG.logDebug( StringTools.concat( 400, "req-env: ", envelope.getMin().getX(), " ",
232                                                      envelope.getMin().getY(), " - ", envelope.getMax().getX(), " ",
233                                                      envelope.getMax().getY(), " lvl: ", level, " typ: ", metaTyp,
234                                                      " row/cell-env: ", xMinCell, " ", yMinCell, " - ", xMaxCell, " ",
235                                                      yMaxCell, " dx/y: ", xDiffPx, "/", yDiffPx ) );
236                }
237    
238                // TODO: grit: testen und entfernen
239                // if ( xMinCell < 0 )
240                // xMinCell = 0;
241                // if ( yMinCell < 0 )
242                // yMinCell = 0;
243                // if ( xMaxCell < 0 )
244                // xMaxCell = 0;
245                // if ( yMaxCell < 0 )
246                // yMaxCell = 0;
247    
248                img = jGeoRaster.getRasterImage( connection, level, xMinCell, yMinCell, xMaxCell, yMaxCell );
249    
250                /*
251                 * rearange returned image in new image with size of request
252                 */
253                if ( img != null ) {
254                    /*
255                     * 1) calculate result bbox in nativ/georaster-level resulution (m/px from oracle georaster) 2) convert
256                     * to outer bbox 3) calculate result resulution (m/px from result) 4) convert from outer bbox to bbox in
257                     * result resulution (middle px) 5) calculate position on result-image (px-box) 6) draw (maybe streched)
258                     * returned image inside the px-box
259                     */
260    
261                    // 1a) returned cell values
262                    int rcMinX = ( xMinCell < 0 ) ? 0 : xMinCell;
263                    int rcMinY = ( yMinCell < 0 ) ? 0 : yMinCell;
264                    int rcMaxX = rcMinX + ( img.getWidth() - 1 );
265                    int rcMaxY = rcMinY + ( img.getHeight() - 1 );
266    
267                    // 1b) cell values to world
268                    double rwMinX = metaObj.getX( (int) ( rcMinX * sc ), (int) ( rcMinY * sc ) );
269                    double rwMinY = metaObj.getY( (int) ( rcMinX * sc ), (int) ( rcMinY * sc ) );
270                    double rwMaxX = metaObj.getX( (int) ( rcMaxX * sc ), (int) ( rcMaxY * sc ) );
271                    double rwMaxY = metaObj.getY( (int) ( rcMaxX * sc ), (int) ( rcMaxY * sc ) );
272    
273                    // 2) convert to edges (see UL/CC handling above)
274                    if ( "UL".equals( metaTyp ) ) {
275                        rwMaxY -= ( yDiffPx + yDiffPx );
276                        rwMaxX += ( xDiffPx + xDiffPx );
277                    } else {
278                        rwMinX -= xDiffPx;
279                        rwMinY += yDiffPx;
280                        rwMaxX += xDiffPx;
281                        rwMaxY -= yDiffPx;
282                    }
283    
284                    // 3) calculate result resulution (m/px)
285                    double resDiffX = envelope.getWidth() / (double) width / 2.0d;
286                    double resDiffY = envelope.getHeight() / (double) height / 2.0d;
287    
288                    // 4) convert outer bbox to middle of pixel values
289                    rwMinX -= resDiffX;
290                    rwMinY += resDiffY;
291                    rwMaxX += resDiffX;
292                    rwMaxY -= resDiffY;
293    
294                    GeoTransform wld2scr = new WorldToScreenTransform( envelope.getMin().getX(), envelope.getMin().getY(),
295                                                                       envelope.getMax().getX(), envelope.getMax().getY(),
296                                                                       0, 0, width - 1, height - 1 );
297    
298                    // 5) calculate pixel position of returned fragment
299                    int scMinX = (int) Math.round( wld2scr.getDestX( rwMinX ) );
300                    int scMinY = (int) Math.round( wld2scr.getDestY( rwMinY ) );
301                    int scMaxX = (int) Math.round( wld2scr.getDestX( rwMaxX ) );
302                    int scMaxY = (int) Math.round( wld2scr.getDestY( rwMaxY ) );
303    
304                    int scWidth = ( scMaxX - scMinX ) + 1;
305                    int scHeight = ( scMaxY - scMinY ) + 1;
306    
307                    // 6) draw returned image into result image
308                    BufferedImage bimg = new BufferedImage( Math.round( width ), Math.round( height ),
309                                                            BufferedImage.TYPE_INT_ARGB );
310                    Graphics2D bg = bimg.createGraphics();
311    
312                    bg.drawImage( (Image) img, scMinX, scMinY, scWidth, scHeight, null );
313                    bg.dispose();
314                    img = bimg;
315                }
316    
317            } catch ( SQLException e1 ) {
318                String s = StringTools.concat( 1000, e1.getMessage(), " ", rasterTable, "; ", rasterRDT, "; ",
319                                               geoRasterCol, "; ", identification, "; level: ", level );
320                LOG.logError( s, e1 );
321    
322                throw new RuntimeException( s );
323            } catch ( Exception e ) {
324                LOG.logError( "error reading georaster", e );
325                throw new RuntimeException( e );
326            }
327            return img;
328        }
329    
330        /**
331         * 
332         * @param connection
333         * @param rasterRDT
334         * @param rasterTable
335         * @param geoRasterCol
336         * @param rasterID
337         * @return
338         * @throws SQLException
339         */
340        private static STRUCT readGeoRasterMetadata( Connection connection, String rasterRDT, String rasterTable,
341                                                     String geoRasterCol, int rasterID )
342                                throws SQLException {
343            PreparedStatement ps = connection.prepareStatement( "select " + geoRasterCol + " from " + rasterTable
344                                                                + " a where a." + geoRasterCol + ".rasterid = " + rasterID
345                                                                + " and a." + geoRasterCol + ".rasterdatatable = '"
346                                                                + rasterRDT.toUpperCase() + "'" );
347            ResultSet resultset = ps.executeQuery();
348            if ( !resultset.next() ) {
349                throw new SQLException( "No GeoRaster object exists at rasterid = " + rasterID + ", RDT = " + rasterRDT );
350            }
351    
352            STRUCT struct = (STRUCT) resultset.getObject( geoRasterCol.toUpperCase() );
353            resultset.close();
354            return struct;
355        }
356    
357        /**
358         * returns the rasterID of the requested GeoRaster
359         * 
360         * @param connection
361         * @param identification
362         * @param sql
363         * @return
364         * @throws SQLException
365         * @throws GeoRasterException
366         */
367        private static int readRasterID( Connection connection, String identification, String rasterTable,
368                                         String geoRasterCol )
369                                throws SQLException, GeoRasterException {
370            String sql = "SELECT  a." + geoRasterCol + ".rasterid FROM " + rasterTable + " a where " + identification;
371            Statement stmt = connection.createStatement();
372            ResultSet rs = stmt.executeQuery( sql );
373            if ( !rs.next() ) {
374                throw new GeoRasterException( "Georaster with identification = " + identification + " not found!" );
375            }
376            int rasterID = rs.getInt( 1 );
377            stmt.close();
378            rs.close();
379            return rasterID;
380        }
381    }