001    //$HeadURL: https://svn.wald.intevation.org/svn/deegree/base/branches/2.3_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: 20437 $
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: 20437 $, $Date: 2009-10-29 09:49:03 +0100 (Do, 29. Okt 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 level
092         *            requested level (resolution)
093         * @return
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         *            connnection 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 georaster col
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         * @return
131         * @throws SQLException
132         * @throws IOException
133         * @throws GeoRasterException
134         * @throws Exception
135         */
136        public static RenderedImage exportRaster( Connection connection, Envelope envelope, String rasterRDT,
137                                                  String rasterTable, String geoRasterCol, String identification,
138                                                  int level, float width, float height )
139                                throws Exception {
140            RenderedImage img = null;
141            try {
142    
143                int rasterID = readRasterID( connection, identification, rasterTable, geoRasterCol );
144    
145                STRUCT struct = readGeoRasterMetadata( connection, rasterRDT, rasterTable, geoRasterCol, rasterID );
146    
147                int major = connection.getMetaData().getDriverMajorVersion();
148                int minor = connection.getMetaData().getDriverMinorVersion();
149    
150                JGeoRaster jGeoRaster = null;
151                if ( major == 10 && minor == 1 ) {
152                    // synthax for Oracle 10g R1
153                    Class<?>[] clzz = new Class[] { STRUCT.class };
154                    Method method = JGeoRaster.class.getMethod( "load", clzz );
155                    jGeoRaster = (JGeoRaster) method.invoke( null, new Object[] { struct } );
156                    jGeoRaster = JGeoRaster.load( struct );
157                } else if ( major == 10 && minor == 2 ) {
158                    // synthax for Oracle 10g R2
159                    Class<?>[] clzz = new Class[] { STRUCT.class, Connection.class, boolean.class };
160                    Method method = JGeoRaster.class.getMethod( "load", clzz );
161                    Object[] params = new Object[] { struct, connection, false };
162                    jGeoRaster = (JGeoRaster) method.invoke( null, params );
163                } else {
164                    String s = StringTools.concat( 250, "Oracle driver ", major, ".", minor,
165                                                   " currently not supported for using Georaster functionality. (use 10.1 or 10.2)" );
166                    throw new InvalidParameterValueException( s );
167                }
168                jGeoRaster.setViewerUse( true );
169                Properties props = jGeoRaster.getProperties();
170    
171                int maxWidth = Integer.parseInt( props.getProperty( "rasterInfo/dimensionSize_column" ) );
172                int maxHeight = Integer.parseInt( props.getProperty( "rasterInfo/dimensionSize_row" ) );
173    
174                JGeoRasterMeta metaObj = jGeoRaster.getMetadataObject();
175    
176                // retieve meta-information how row/cells are referenced
177                String metaTyp = "CC";
178                String metaTxt = jGeoRaster.getMetadataString();
179                if ( metaTxt != null
180                     && metaTxt.indexOf( "<modelCoordinateLocation>UPPERLEFT</modelCoordinateLocation>" ) != -1 )
181                    metaTyp = "UL";
182    
183                double xMin = metaObj.getX( 0, 0 );
184                double xMax = metaObj.getX( maxWidth - 1, maxHeight - 1 );
185                double sc = Math.pow( 2, level );
186                double yMin = metaObj.getY( 0, 0 );
187                double yMax = metaObj.getY( maxWidth - 1, maxHeight - 1 );
188    
189                double xDiffPx = Math.abs( ( metaObj.getX( 1, 1 ) - xMin ) / 2 );
190                double yDiffPx = Math.abs( ( metaObj.getY( 1, 1 ) - yMin ) / 2 );
191    
192                // LOG.logDebug(StringTools.concat(350, "georaster-extend ",
193                // xMin, " ", yMin, " - ", xMax, " ", yMax, " typ: ", metaTyp ));
194    
195                /*
196                 * Difference between UL and CC handling
197                 * 
198                 * CC: Extend BBOX from middle-middel-pixel values to outer extend (default) (A -> B)
199                 * 
200                 * B-----* *-----B |\ | | /| | \ | . | / | | A | . | A | | | . | | | | | | *-----* *-----* ... ... *-----*
201                 * *-----* | | | | | | . | | | A | . | A | | / | . | \ | |/ | | \| B-----* *-----B
202                 * 
203                 * UL: Extend BBOX from upper-left-pixel values to outer extend (C=A=B; A->B)
204                 * 
205                 * C-----* A->->-B | | | | | | . | | | | . | | | | . | | | | | | *-----* *-----* ... ... A-----* A-----* | |
206                 * |\ | v | . | \ | | | . | \ | v | . | \ | | | | \| B-----A *-----B
207                 */
208                if ( "UL".equals( metaTyp ) ) {
209                    // koordinatenreferenzierung oben-links
210                    yMax -= ( yDiffPx + yDiffPx );
211                    xMax += ( xDiffPx + xDiffPx );
212                } else {
213                    // koordinatenreferenzierung center-center
214                    xMin -= xDiffPx;
215                    yMin += yDiffPx;
216                    xMax += xDiffPx;
217                    yMax -= yDiffPx;
218                }
219    
220                WorldToScreenTransform wld2ora = new WorldToScreenTransform( xMin, yMin, xMax, yMax, 0, 0, maxWidth - 1,
221                                                                             maxHeight - 1 );
222    
223                int xMinCell = (int) Math.round( wld2ora.getDestX( envelope.getMin().getX() ) / sc );
224                int xMaxCell = (int) Math.round( wld2ora.getDestX( envelope.getMax().getX() ) / sc );
225                int yMaxCell = (int) Math.round( wld2ora.getDestY( envelope.getMin().getY() ) / sc );
226                int yMinCell = (int) Math.round( wld2ora.getDestY( envelope.getMax().getY() ) / sc );
227    
228                if ( LOG.isDebug() ) {
229                    LOG.logDebug( StringTools.concat( 400, "req-env: ", envelope.getMin().getX(), " ",
230                                                      envelope.getMin().getY(), " - ", envelope.getMax().getX(), " ",
231                                                      envelope.getMax().getY(), " lvl: ", level, " typ: ", metaTyp,
232                                                      " row/cell-env: ", xMinCell, " ", yMinCell, " - ", xMaxCell, " ",
233                                                      yMaxCell, " dx/y: ", xDiffPx, "/", yDiffPx ) );
234                }
235    
236                // TODO: grit: testen und entfernen
237                // if ( xMinCell < 0 )
238                // xMinCell = 0;
239                // if ( yMinCell < 0 )
240                // yMinCell = 0;
241                // if ( xMaxCell < 0 )
242                // xMaxCell = 0;
243                // if ( yMaxCell < 0 )
244                // yMaxCell = 0;
245    
246                img = jGeoRaster.getRasterImage( connection, level, xMinCell, yMinCell, xMaxCell, yMaxCell );
247    
248                /*
249                 * rearange returned image in new image with size of request
250                 */
251                if ( img != null ) {
252                    /*
253                     * 1) calculate result bbox in nativ/georaster-level resulution (m/px from oracle georaster) 2) convert
254                     * to outer bbox 3) calculate result resulution (m/px from result) 4) convert from outer bbox to bbox in
255                     * result resulution (middle px) 5) calculate position on result-image (px-box) 6) draw (maybe streched)
256                     * returned image inside the px-box
257                     */
258    
259                    // 1a) returned cell values
260                    int rcMinX = ( xMinCell < 0 ) ? 0 : xMinCell;
261                    int rcMinY = ( yMinCell < 0 ) ? 0 : yMinCell;
262                    int rcMaxX = rcMinX + ( img.getWidth() - 1 );
263                    int rcMaxY = rcMinY + ( img.getHeight() - 1 );
264    
265                    // 1b) cell values to world
266                    double rwMinX = metaObj.getX( (int) ( rcMinX * sc ), (int) ( rcMinY * sc ) );
267                    double rwMinY = metaObj.getY( (int) ( rcMinX * sc ), (int) ( rcMinY * sc ) );
268                    double rwMaxX = metaObj.getX( (int) ( rcMaxX * sc ), (int) ( rcMaxY * sc ) );
269                    double rwMaxY = metaObj.getY( (int) ( rcMaxX * sc ), (int) ( rcMaxY * sc ) );
270    
271                    // 2) convert to edges (see UL/CC handling above)
272                    if ( "UL".equals( metaTyp ) ) {
273                        rwMaxY -= ( yDiffPx + yDiffPx );
274                        rwMaxX += ( xDiffPx + xDiffPx );
275                    } else {
276                        rwMinX -= xDiffPx;
277                        rwMinY += yDiffPx;
278                        rwMaxX += xDiffPx;
279                        rwMaxY -= yDiffPx;
280                    }
281    
282                    // 3) calculate result resulution (m/px)
283                    double resDiffX = envelope.getWidth() / (double) width / 2.0d;
284                    double resDiffY = envelope.getHeight() / (double) height / 2.0d;
285    
286                    // 4) convert outer bbox to middle of pixel values
287                    rwMinX -= resDiffX;
288                    rwMinY += resDiffY;
289                    rwMaxX += resDiffX;
290                    rwMaxY -= resDiffY;
291    
292                    GeoTransform wld2scr = new WorldToScreenTransform( envelope.getMin().getX(), envelope.getMin().getY(),
293                                                                       envelope.getMax().getX(), envelope.getMax().getY(),
294                                                                       0, 0, width - 1, height - 1 );
295    
296                    // 5) calculate pixel position of returned fragment
297                    int scMinX = (int) Math.round( wld2scr.getDestX( rwMinX ) );
298                    int scMinY = (int) Math.round( wld2scr.getDestY( rwMinY ) );
299                    int scMaxX = (int) Math.round( wld2scr.getDestX( rwMaxX ) );
300                    int scMaxY = (int) Math.round( wld2scr.getDestY( rwMaxY ) );
301    
302                    int scWidth = ( scMaxX - scMinX ) + 1;
303                    int scHeight = ( scMaxY - scMinY ) + 1;
304    
305                    // 6) draw returned image into result image
306                    BufferedImage bimg = new BufferedImage( Math.round( width ), Math.round( height ),
307                                                            BufferedImage.TYPE_INT_ARGB );
308                    Graphics2D bg = bimg.createGraphics();
309    
310                    bg.drawImage( (Image) img, scMinX, scMinY, scWidth, scHeight, null );
311                    bg.dispose();
312                    img = bimg;
313                }
314    
315            } catch ( SQLException e1 ) {
316                String s = StringTools.concat( 1000, e1.getMessage(), " ", rasterTable, "; ", rasterRDT, "; ",
317                                               geoRasterCol, "; ", identification, "; level: ", level );
318                LOG.logError( s, e1 );
319    
320                throw new RuntimeException( s );
321            } catch ( Exception e ) {
322                LOG.logError( "error reading georaster", e );
323                throw new RuntimeException( e );
324            }
325            return img;
326        }
327    
328        /**
329         * 
330         * @param connection
331         * @param rasterRDT
332         * @param rasterTable
333         * @param geoRasterCol
334         * @param rasterID
335         * @return
336         * @throws SQLException
337         */
338        private static STRUCT readGeoRasterMetadata( Connection connection, String rasterRDT, String rasterTable,
339                                                     String geoRasterCol, int rasterID )
340                                throws SQLException {
341            PreparedStatement ps = connection.prepareStatement( "select " + geoRasterCol + " from " + rasterTable
342                                                                + " a where a." + geoRasterCol + ".rasterid = " + rasterID
343                                                                + " and a." + geoRasterCol + ".rasterdatatable = '"
344                                                                + rasterRDT.toUpperCase() + "'" );
345            ResultSet resultset = ps.executeQuery();
346            if ( !resultset.next() ) {
347                throw new SQLException( "No GeoRaster object exists at rasterid = " + rasterID + ", RDT = " + rasterRDT );
348            }
349    
350            STRUCT struct = (STRUCT) resultset.getObject( geoRasterCol.toUpperCase() );
351            resultset.close();
352            return struct;
353        }
354    
355        /**
356         * returns the rasterID of the requested GeoRaster
357         * 
358         * @param connection
359         * @param identification
360         * @param sql
361         * @return
362         * @throws SQLException
363         * @throws GeoRasterException
364         */
365        private static int readRasterID( Connection connection, String identification, String rasterTable,
366                                         String geoRasterCol )
367                                throws SQLException, GeoRasterException {
368            String sql = "SELECT  a." + geoRasterCol + ".rasterid FROM " + rasterTable + " a where " + identification;
369            Statement stmt = connection.createStatement();
370            ResultSet rs = stmt.executeQuery( sql );
371            if ( !rs.next() ) {
372                throw new GeoRasterException( "Georaster with identification = " + identification + " not found!" );
373            }
374            int rasterID = rs.getInt( 1 );
375            stmt.close();
376            rs.close();
377            return rasterID;
378        }
379    }