001    //$HeadURL$
002    /*----------------    FILE HEADER  ------------------------------------------
003     This file is part of deegree.
004     Copyright (C) 2001-2008 by:
005     Department of Geography, University of Bonn
006     http://www.giub.uni-bonn.de/deegree/
007     lat/lon GmbH
008     http://www.lat-lon.de
009    
010     This library is free software; you can redistribute it and/or
011     modify it under the terms of the GNU Lesser General Public
012     License as published by the Free Software Foundation; either
013     version 2.1 of the License, or (at your option) any later version.
014     This library is distributed in the hope that it will be useful,
015     but WITHOUT ANY WARRANTY; without even the implied warranty of
016     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017     Lesser General Public License for more details.
018     You should have received a copy of the GNU Lesser General Public
019     License along with this library; if not, write to the Free Software
020     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
021     Contact:
022    
023     Andreas Poth
024     lat/lon GmbH
025     Aennchenstr. 19
026     53177 Bonn
027     Germany
028     E-Mail: poth@lat-lon.de
029    
030     Prof. Dr. Klaus Greve
031     Department of Geography
032     University of Bonn
033     Meckenheimer Allee 166
034     53115 Bonn
035     Germany
036     E-Mail: greve@giub.uni-bonn.de
037     ---------------------------------------------------------------------------*/
038    
039    package org.deegree.tools.raster;
040    
041    import static java.io.File.separator;
042    
043    import java.io.File;
044    import java.io.FileOutputStream;
045    import java.io.IOException;
046    import java.io.PrintStream;
047    import java.net.URI;
048    import java.net.URISyntaxException;
049    import java.net.URL;
050    import java.security.InvalidParameterException;
051    import java.sql.Connection;
052    import java.sql.PreparedStatement;
053    import java.sql.SQLException;
054    import java.sql.Statement;
055    import java.util.Arrays;
056    import java.util.HashMap;
057    import java.util.Iterator;
058    import java.util.List;
059    import java.util.Map;
060    import java.util.Properties;
061    
062    import javax.xml.transform.TransformerException;
063    
064    import org.deegree.datatypes.QualifiedName;
065    import org.deegree.framework.log.ILogger;
066    import org.deegree.framework.log.LoggerFactory;
067    import org.deegree.framework.util.ConvenienceFileFilter;
068    import org.deegree.framework.util.FileUtils;
069    import org.deegree.framework.util.StringTools;
070    import org.deegree.framework.xml.XMLFragment;
071    import org.deegree.framework.xml.XSLTDocument;
072    import org.deegree.io.DBConnectionPool;
073    import org.deegree.io.DBPoolException;
074    import org.deegree.io.datastore.sql.postgis.PGgeometryAdapter;
075    import org.deegree.io.dbaseapi.DBaseException;
076    import org.deegree.io.shpapi.shape_new.ShapeFile;
077    import org.deegree.io.shpapi.shape_new.ShapeFileReader;
078    import org.deegree.model.feature.Feature;
079    import org.deegree.model.feature.FeatureCollection;
080    import org.deegree.model.spatialschema.Geometry;
081    import org.deegree.model.spatialschema.GeometryException;
082    import org.xml.sax.SAXException;
083    
084    /**
085     * 
086     * 
087     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
088     * @author last edited by: $Author: poth $
089     * 
090     * @version. $Revision: 6251 $, $Date: 2007-03-19 16:59:28 +0100 (Mo, 19 Mrz 2007) $
091     */
092    public class ImportIndexIntoDB {
093    
094        private static ILogger LOG = LoggerFactory.getLogger( ImportIndexIntoDB.class );
095    
096        private static final String createPG = "create_wcs_table_template_postgis.sql";
097    
098        private static final String indexPG = "create_wcs_table_index_template_postgis.sql";
099    
100        private static final String createOrcl = "create_wcs_table_template_oracle.sql";
101    
102        private static final String indexOrcl = "create_wcs_index_table_template_oracle.sql";
103    
104        private static URI app = null;
105    
106        static {
107            try {
108                app = new URI( "http://www.deegree.org/app" );
109            } catch ( URISyntaxException e ) {
110                e.printStackTrace();
111            }
112        }
113    
114        private String table;
115    
116        private String user;
117    
118        private String pw;
119    
120        private String driver;
121    
122        private String url;
123    
124        private String rootDir;
125    
126        private File configurationFile;
127    
128        /**
129         * @param driver
130         * @param url
131         * @param user
132         * @param pw
133         * @param table
134         * @param rootDir
135         */
136        public ImportIndexIntoDB( String driver, String url, String user, String pw, String table, String rootDir ) {
137            this.driver = driver;
138            this.url = url;
139            this.user = user;
140            this.pw = pw;
141            this.table = table;
142            this.rootDir = rootDir;
143            ConvenienceFileFilter ff = new ConvenienceFileFilter( false, "XML" );
144            File dir = new File( this.rootDir );
145            configurationFile = dir.listFiles( ff )[0];
146        }
147    
148        /**
149         * main method to perform indexing of raster tile via a spatial DB
150         * 
151         * @throws IOException
152         * @throws DBPoolException
153         * @throws SQLException
154         * @throws DBaseException
155         * @throws GeometryException
156         * @throws SAXException
157         * @throws TransformerException
158         */
159        public void perform()
160                                throws IOException, DBPoolException, SQLException, DBaseException, GeometryException,
161                                SAXException, TransformerException {
162            createTables();
163            fillIndexTable();
164            // index will be created after data has been added to increase performance
165            createIndex();
166            adaptConfiguration();
167        }
168    
169        /**
170         * fill the pyramid table with informations about the assoziation between levels and scales
171         * 
172         * @param map
173         * @throws DBPoolException
174         * @throws SQLException
175         */
176        private void fillPyramidTable( Map<File, PyramidHelper> map )
177                                throws DBPoolException, SQLException {
178            DBConnectionPool pool = DBConnectionPool.getInstance();
179            Connection con = pool.acquireConnection( driver, url, user, pw );
180            Iterator<PyramidHelper> iterator = map.values().iterator();
181            while ( iterator.hasNext() ) {
182                PyramidHelper helper = iterator.next();
183                String sql = StringTools.concat( 200, "INSERT INTO ", table,
184                                                 "_pyr (level,minscale,maxscale) values (?,?,?)" );
185                PreparedStatement stmt = con.prepareStatement( sql );
186                stmt.setInt( 1, helper.level );
187                stmt.setFloat( 2, helper.minscale );
188                stmt.setFloat( 3, helper.maxscale );
189                stmt.execute();
190                stmt.close();
191            }
192            LOG.logInfo( "pyramid table filled!" );
193    
194        }
195    
196        /**
197         * creates DB-indexes for pyramid and tile-index tables
198         * 
199         * @throws IOException
200         * @throws DBPoolException
201         * @throws SQLException
202         */
203        private void createIndex()
204                                throws IOException, DBPoolException, SQLException {
205            String template = null;
206            if ( driver.toUpperCase().indexOf( "ORACLE" ) > -1 ) {
207                template = indexOrcl;
208            } else if ( driver.toUpperCase().indexOf( "POSTGRES" ) > -1 ) {
209                template = indexPG;
210            }
211    
212            StringBuffer sb = FileUtils.readTextFile( ImportIndexIntoDB.class.getResource( template ) );
213            String tmp = StringTools.replace( sb.toString(), "$TABLE$", table.toLowerCase(), true );
214            String[] sql = parseSQLStatements( tmp );
215    
216            DBConnectionPool pool = DBConnectionPool.getInstance();
217            Connection con = pool.acquireConnection( driver, url, user, pw );
218            Statement stmt = con.createStatement();
219            for ( int i = 0; i < sql.length; i++ ) {
220                stmt.execute( sql[i] );
221            }
222            stmt.close();
223            pool.releaseConnection( con, driver, url, user, pw );
224            LOG.logInfo( "indexes created!" );
225    
226        }
227    
228        /**
229         * fills the index table with informations about raster tiles, their spatial extent, assigened
230         * level and assigned file
231         * 
232         * @throws IOException
233         * @throws DBaseException
234         * @throws DBPoolException
235         * @throws SQLException
236         * @throws GeometryException
237         */
238        private void fillIndexTable()
239                                throws IOException, DBaseException, DBPoolException, SQLException, GeometryException {
240            DBConnectionPool pool = DBConnectionPool.getInstance();
241            Connection con = pool.acquireConnection( driver, url, user, pw );
242    
243            ConvenienceFileFilter ff = new ConvenienceFileFilter( false, "SHP" );
244            File dir = new File( rootDir );
245            File[] files = dir.listFiles( ff );
246            Map<File, PyramidHelper> map = createLevelMap( files );
247            fillPyramidTable( map );
248            for ( int i = 0; i < files.length; i++ ) {
249                LOG.logInfo( "import: ", files[i].getAbsolutePath() );
250                String tmp = files[i].getAbsolutePath();
251                tmp = tmp.substring( 0, tmp.length() - 4 );
252                ShapeFileReader sfr = new ShapeFileReader( tmp );
253                ShapeFile sf = sfr.read();
254                FeatureCollection fc = sf.getFeatureCollection();
255                for ( int j = 0; j < fc.size(); j++ ) {
256                    String sql = StringTools.concat( 200, "INSERT INTO ", table,
257                                                     " (level, dir, file, bbox) values(?,?,?,?)" );
258                    PreparedStatement stmt = con.prepareStatement( sql );
259                    Object[] values = createInsert( fc.getFeature( j ) );
260                    stmt.setInt( 1, map.get( files[i] ).level );
261                    stmt.setString( 2, (String) values[1] );
262                    stmt.setString( 3, (String) values[2] );
263                    stmt.setObject( 4, values[3] );
264                    stmt.execute();
265                    stmt.close();
266                }
267    
268            }
269    
270            pool.releaseConnection( con, driver, url, user, pw );
271            LOG.logInfo( "index table filled!" );
272    
273        }
274    
275        /**
276         * creates pyramid level map; assigning each file to a level starting at 0
277         * 
278         * @param files
279         * @return
280         */
281        private Map<File, PyramidHelper> createLevelMap( File[] files ) {
282            PyramidHelper[] phelper = new PyramidHelper[files.length];
283            for ( int i = 0; i < files.length; i++ ) {
284                String tmp = files[i].getName().substring( 2, files[i].getName().length() - 4 );
285                float scale = Float.parseFloat( tmp );
286                PyramidHelper helper = new PyramidHelper();
287                helper.file = files[i];
288                helper.scale = scale;
289                phelper[i] = helper;
290            }
291            Arrays.sort( phelper );
292            Map<File, PyramidHelper> map = new HashMap<File, PyramidHelper>();
293            for ( int i = 0; i < phelper.length; i++ ) {
294                phelper[i].level = i;
295                if ( i == 0 ) {
296                    phelper[i].minscale = 0;
297                } else {
298                    phelper[i].minscale = phelper[i - 1].scale;
299                }
300                if ( i == phelper.length - 1 ) {
301                    phelper[i].maxscale = 99999999999F;
302                } else {
303                    phelper[i].maxscale = phelper[i].scale;
304                }
305                map.put( phelper[i].file, phelper[i] );
306            }
307            LOG.logInfo( "pyramid level assoziation:", map );
308            return map;
309        }
310    
311        /**
312         * 
313         * @param feature
314         * @return values to insert for a raster tile
315         * @throws GeometryException
316         */
317        private Object[] createInsert( Feature feature )
318                                throws GeometryException {
319            Object[] values = new Object[4];
320            values[1] = feature.getProperties( new QualifiedName( "FOLDER", app ) )[0].getValue();
321            values[2] = feature.getProperties( new QualifiedName( "FILENAME", app ) )[0].getValue();
322            Geometry geom = feature.getDefaultGeometryPropertyValue();
323            values[3] = PGgeometryAdapter.export( geom, -1 );
324            return values;
325        }
326    
327        /**
328         * creates data and pyramid table
329         * 
330         * @throws IOException
331         * @throws DBPoolException
332         * @throws SQLException
333         */
334        private void createTables()
335                                throws IOException, DBPoolException, SQLException {
336            String template = null;
337            if ( driver.toUpperCase().indexOf( "ORACLE" ) > -1 ) {
338                template = createOrcl;
339            } else if ( driver.toUpperCase().indexOf( "POSTGRES" ) > -1 ) {
340                template = createPG;
341            }
342            StringBuffer sb = FileUtils.readTextFile( ImportIndexIntoDB.class.getResource( template ) );
343            String tmp = StringTools.replace( sb.toString(), "$TABLE$", table.toLowerCase(), true );
344            String[] sql = parseSQLStatements( tmp );
345    
346            DBConnectionPool pool = DBConnectionPool.getInstance();
347            Connection con = pool.acquireConnection( driver, url, user, pw );
348            Statement stmt = con.createStatement();
349            for ( int i = 0; i < sql.length; i++ ) {
350                try {
351                    stmt.execute( sql[i] );
352                } catch ( Exception e ) {
353                    LOG.logWarning( e.getMessage() );
354                }
355            }
356            stmt.close();
357            pool.releaseConnection( con, driver, url, user, pw );
358            LOG.logInfo( "tables created!" );
359    
360        }
361    
362        /**
363         * 
364         * @param tmp
365         * @return SQL statements parsed from a file/string
366         */
367        private String[] parseSQLStatements( String tmp ) {
368            List<String> list = StringTools.toList( tmp, ";", false );
369            String[] sql = new String[list.size()];
370            for ( int i = 0; i < sql.length; i++ ) {
371                sql[i] = list.get( i ).trim();
372            }
373            return sql;
374        }
375    
376        /**
377         * adapts the wcs configuration for a coverage to use the created db-based index instead of a
378         * shapefile base one.
379         * 
380         * @throws IOException
381         * @throws SAXException
382         * @throws TransformerException
383         */
384        private void adaptConfiguration()
385                                throws IOException, SAXException, TransformerException {
386            URL url = ImportIndexIntoDB.class.getResource( "wcsconfiguration.xsl" );
387            XSLTDocument xslt = new XSLTDocument( url );
388            XMLFragment xml = new XMLFragment( configurationFile.toURI().toURL() );
389            xml = xslt.transform( xml );
390            Map<String, String> params = new HashMap<String, String>();
391            params.put( "TABLE", table );
392            params.put( "USER", user );
393            params.put( "PASSWORD", pw );
394            params.put( "DRIVER", driver );
395            params.put( "URL", this.url );
396            xml = xslt.transform( xml, XMLFragment.DEFAULT_URL, null, params );
397            FileOutputStream fos = new FileOutputStream( configurationFile );
398            xml.write( fos );
399            fos.close();
400        }
401    
402        /**
403         * @param args
404         */
405        public static void main( String[] args )
406                                throws Exception {
407    
408            try {
409                Properties map = new Properties();
410                for ( int i = 0; i < args.length; ) {
411                    String first = args[i++];
412                    if ( "?".equals( first ) || "-h".equals( first ) || "-help".equals( first ) ) {
413                        printHelp();
414                        System.exit( 0 );
415                    }
416                    map.put( first, args[i++] );
417                }
418    
419                // set up stderr/stdout redirection
420                String redirect = map.getProperty( "-redirect" );
421                if ( redirect != null && redirect.equals( "true" ) ) {
422                    String rootDir = map.getProperty( "-rootDir" );
423                    File f = new File( rootDir + separator + "dbindexer.log" );
424                    PrintStream out = new PrintStream( new FileOutputStream( f ) );
425                    System.setOut( out );
426                    System.setErr( out );
427                }
428    
429                try {
430                    validate( map );
431                } catch ( InvalidParameterException ipe ) {
432                    LOG.logError( ipe.getMessage() );
433                    printHelp();
434                    System.exit( 1 );
435                }
436                LOG.logDebug( "Resulting commandline arguments and their values {argument=value, ...}: " + map.toString() );
437    
438                String table = map.getProperty( "-table" );
439                String user = map.getProperty( "-user" );
440                String pw = map.getProperty( "-password" );
441                String driver = map.getProperty( "-driver" );
442                String url = map.getProperty( "-url" );
443                String rootDir = map.getProperty( "-rootDir" );
444    
445                ImportIndexIntoDB importer = new ImportIndexIntoDB( driver, url, user, pw, table, rootDir );
446                importer.perform();
447            } catch ( Exception e ) {
448                e.printStackTrace();
449                System.exit( 1 );
450            }
451    
452            System.exit( 0 );
453        }
454    
455        /**
456         * 
457         * @param map
458         * @throws InvalidParameterException
459         */
460        private static void validate( Properties map )
461                                throws InvalidParameterException {
462            if ( map.getProperty( "-table" ) == null ) {
463                throw new InvalidParameterException( "-table must be set" );
464            }
465            if ( map.getProperty( "-user" ) == null ) {
466                throw new InvalidParameterException( "-user must be set" );
467            }
468            if ( map.getProperty( "-password" ) == null ) {
469                throw new InvalidParameterException( "-password must be set" );
470            }
471            if ( map.getProperty( "-driver" ) == null ) {
472                throw new InvalidParameterException( "-driver must be set" );
473            }
474            if ( map.getProperty( "-url" ) == null ) {
475                throw new InvalidParameterException( "-url must be set" );
476            }
477            if ( map.getProperty( "-rootDir" ) == null ) {
478                throw new InvalidParameterException( "-rootDir must be set" );
479            }
480    
481        }
482    
483        /**
484         * 
485         * 
486         */
487        private static void printHelp() {
488            // TODO Auto-generated method stub
489            System.out.println( "no help available at the moment" );
490        }
491    
492        /**
493         * 
494         * 
495         * 
496         * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
497         * @author last edited by: $Author: poth $
498         * 
499         * @version. $Revision: 6251 $, $Date: 2007-03-19 16:59:28 +0100 (Mo, 19 Mrz 2007) $
500         */
501        class PyramidHelper implements Comparable<PyramidHelper> {
502    
503            File file;
504    
505            float scale;
506    
507            float minscale;
508    
509            float maxscale;
510    
511            int level;
512    
513            /**
514             * @param o
515             */
516            public int compareTo( PyramidHelper o ) {
517                if ( o.scale < scale ) {
518                    return 1;
519                } else if ( o.scale > scale ) {
520                    return -1;
521                }
522                return 0;
523            }
524    
525            /**
526             * @return string representation
527             */
528            public String toString() {
529                return StringTools.concat( 260, file.getName(), ' ', level, ' ', scale );
530            }
531    
532        }
533    
534    }