001    //$HeadURL: svn+ssh://rbezema@svn.wald.intevation.org/deegree/base/branches/2.2_testing/src/org/deegree/io/quadtree/DBQuadtree.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     53177 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    package org.deegree.io.quadtree;
044    
045    import java.sql.Connection;
046    import java.sql.ResultSet;
047    import java.sql.SQLException;
048    import java.sql.Statement;
049    import java.util.ArrayList;
050    import java.util.HashMap;
051    import java.util.List;
052    import java.util.Map;
053    
054    import org.deegree.framework.log.ILogger;
055    import org.deegree.framework.log.LoggerFactory;
056    import org.deegree.io.DBConnectionPool;
057    import org.deegree.io.DBPoolException;
058    import org.deegree.io.JDBCConnection;
059    import org.deegree.model.spatialschema.Envelope;
060    import org.deegree.model.spatialschema.GeometryFactory;
061    import org.deegree.model.spatialschema.Point;
062    
063    /**
064     * 
065     * 
066     * 
067     * @version $Revision: 9342 $
068     * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
069     * @author last edited by: $Author: apoth $
070     * 
071     * @version 1.0. $Revision: 9342 $, $Date: 2007-12-27 13:32:57 +0100 (Do, 27 Dez 2007) $
072     * @param <T>
073     *            the datatype to be used as id
074     * 
075     * @since 2.0
076     */
077    public class DBQuadtree<T> implements Quadtree<T> {
078    
079        private static ILogger LOG = LoggerFactory.getLogger( DBQuadtree.class );
080    
081        private String fk_root;
082    
083        private int depth;
084    
085        private int id = 0;
086    
087        private String indexName = null;
088    
089        private JDBCConnection jdbc = null;
090    
091        private Map<String, DBNode<T>> nodeCache = new HashMap<String, DBNode<T>>( 10000 );
092    
093        private double accuracyX;
094    
095        private double accuracyY;
096    
097        private DBQuadtree.SupportedVersions version;
098    
099        private Connection con = null;
100    
101        private DBConnectionPool pool = null;
102    
103        /**
104         * The <code>SupportedVersions</code> supported by this quatree
105         * 
106         */
107        public static enum SupportedVersions {
108            /**
109             * the old version or 1.0.0
110             */
111            ONE,
112            /**
113             * The new version or "2.0.0"
114             */
115            TWO
116        }
117    
118        /**
119         * Creates a Database with given version.
120         * 
121         * @param id
122         *            of the table which contains the features.
123         * @param indexName
124         *            this name will be used to create the table that stores the nodes of a specific
125         *            quadtree
126         * @param jdbc
127         *            description of database connection
128         * @param version
129         *            of the quadtree, which is usefull for the determination of the layout, if null
130         *            then the version is assumed to be unknown and the old layout is used.
131         * @throws IndexException
132         */
133        public DBQuadtree( int id, String indexName, JDBCConnection jdbc, String version ) throws IndexException {
134            this( id, indexName, jdbc, 0.0001, 0.0001, version );
135        }
136    
137        /**
138         * initializes a quadtree already existing in a database. New items will have a slightly larger
139         * bbox with 0.0001 added to each boundary, this is usefull for point geometries.
140         * 
141         * @param id
142         *            of the table which contains the features.
143         * @param indexName
144         *            this name will be used to create the table that stores the nodes of a specific
145         *            quadtree
146         * @param jdbc
147         *            description of database connection
148         * @throws IndexException
149         *             if the quadtree node with 'id' could not be read from the database.
150         */
151        public DBQuadtree( int id, String indexName, JDBCConnection jdbc ) throws IndexException {
152            this( id, indexName, jdbc, 0.0001, 0.0001, "1.0.0" );
153        }
154    
155        /**
156         * initializes a quadtree already existing in a database
157         * 
158         * @param id
159         *            of the table which contains the features.
160         * @param indexName
161         *            this name will be used to create the table that stores the nodes of a specific
162         *            quadtree
163         * @param jdbc
164         *            description of database connection
165         * @param accuracyX
166         * @param accuracyY
167         * @param version
168         *            of the quadtree, which is usefull for the determination of the layout, if null
169         *            then the version is assumed to be "1.0.0" (the first version) and the old layout
170         *            is used.
171         * @throws IndexException
172         *             if the quadtree node with 'id' could not be read from the database.
173         */
174        public DBQuadtree( int id, String indexName, JDBCConnection jdbc, double accuracyX, double accuracyY, String version )
175                                throws IndexException {
176            this.id = id;
177            this.jdbc = jdbc;
178            this.indexName = indexName;
179            this.accuracyX = accuracyX;
180            this.accuracyY = accuracyY;
181            pool = DBConnectionPool.getInstance();
182            try {
183                con = pool.acquireConnection( jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
184            } catch ( DBPoolException e ) {
185                String msg = "Could not acquire a database connection for the quadtree, no possibility to go on!";
186                LOG.logError( msg + " Cause: " + e.getMessage() );
187                throw new IndexException( msg, e );
188            }
189    
190            if ( version == null || !"2.0.0".equals( version.trim() ) ) {
191                this.version = SupportedVersions.ONE;
192            } else {
193                this.version = SupportedVersions.TWO;
194            }
195            readRootNodeId();
196        }
197    
198        /**
199         * 
200         * @param id
201         * @return node
202         */
203        DBNode<T> getFromCache( String id ) {
204            return nodeCache.get( id );
205        }
206    
207        /**
208         * 
209         * @param node
210         */
211        void addToCache( DBNode<T> node ) {
212            nodeCache.put( node.getId(), node );
213        }
214    
215        /**
216         * 
217         * @param node
218         */
219        DBNode<T> removeFromCache( DBNode<T> node ) {
220            return nodeCache.remove( node.getId() );
221        }
222    
223        /**
224         * inserts a new item into a quadtree
225         * 
226         * @param item
227         * @param envelope
228         */
229        public void insert( T item, Envelope envelope )
230                                throws IndexException {
231            DBNode<T> node = new DBNode<T>( fk_root, this, indexName, jdbc, 1, version );
232            node.insert( item, envelope );
233        }
234    
235        /**
236         * @param item
237         * @param point
238         */
239        public void insert( T item, Point point )
240                                throws IndexException {
241            DBNode<T> node = new DBNode<T>( fk_root, this, indexName, jdbc, 1, version );
242            Envelope envelope = GeometryFactory.createEnvelope( point.getX() - accuracyX, point.getY() - accuracyY,
243                                                                point.getX() + accuracyX, point.getY() + accuracyY, null );
244            node.insert( item, envelope );
245        }
246    
247        /**
248         * 
249         * @param envelope
250         * @return list a items intersecting with the passed envelope
251         */
252        public List<T> query( Envelope envelope )
253                                throws IndexException {
254            LOG.logDebug( "Performing query for envelope: " + envelope );
255            // Thread.dumpStack();
256            List<T> visitor = new ArrayList<T>( 1000 );
257            DBNode<T> node = new DBNode<T>( fk_root, null, this, indexName, jdbc, 1, version );
258            envelope = envelope.createIntersection( node.getEnvelope() );
259            if ( envelope == null ) {
260                LOG.logDebug( "Found no intersection with the root element of the quadtree, returning an emtpy feature collection." );
261                return new ArrayList<T>();
262            }
263            return node.query( envelope, visitor, 1 );
264        }
265    
266        /**
267         * deletes an item from a quadtree
268         * 
269         * @param item
270         */
271        public void deleteItem( T item )
272                                throws IndexException {
273            DBNode<T> root = new DBNode<T>( fk_root, this, indexName, jdbc, 1, version );
274            root.delete( item, root.getEnvelope() );
275        }
276    
277        /**
278         * updates the envelope of an item
279         * 
280         * @param item
281         * @param newBBox
282         */
283        public void update( T item, Envelope newBBox )
284                                throws IndexException {
285            Node<T> root = new DBNode<T>( fk_root, this, indexName, jdbc, 1, version );
286            root.update( item, newBBox );
287        }
288    
289        public void deleteRange( Envelope envelope ) {
290            throw new UnsupportedOperationException();
291            // TODO
292            // and even more magic
293        }
294    
295        /**
296         * @return depth of a quadtree
297         */
298        public int getDepth() {
299            return depth;
300        }
301    
302        /**
303         * @return true if the db connection could be released to the pool, false otherwise.
304         */
305        public boolean releaseConnection() {
306            try {
307                pool.releaseConnection( con, jdbc.getDriver(), jdbc.getURL(), jdbc.getUser(), jdbc.getPassword() );
308            } catch ( DBPoolException e ) {
309                LOG.logError( "Could not release database connection because: " + e.getMessage() );
310                return false;
311            }
312            return true;
313        }
314    
315        /**
316         * reads the root node from the database
317         * 
318         * @throws IndexException
319         *             if the database could not be loaded or no quadtree rootnode was found.
320         */
321        private void readRootNodeId()
322                                throws IndexException {
323    
324            try {
325                StringBuffer sb = new StringBuffer( 200 );
326                sb.append( "Select FK_ROOT, DEPTH from TAB_QUADTREE where ID = " );
327                sb.append( id );
328    
329                Statement stmt = con.createStatement();
330                ResultSet rs = stmt.executeQuery( sb.toString() );
331                if ( rs.next() ) {
332                    fk_root = rs.getString( "FK_ROOT" );
333                    depth = rs.getInt( "DEPTH" );
334                } else {
335                    throw new IndexException( "Could not read FK_ROOT and DEPTH for Quadtree with ID" + id );
336                }
337                rs.close();
338                stmt.close();
339            } catch ( SQLException e ) {
340                throw new IndexException( "Could not load quadtree definition from database because: " + e.getMessage() );
341            }
342    
343        }
344    
345        /**
346         * @return envelope of a quadtree's root node
347         */
348        public Envelope getRootBoundingBox()
349                                throws IndexException {
350            DBNode<T> root = new DBNode<T>( fk_root, this, indexName, jdbc, 1, version );
351            return root.getEnvelope();
352        }
353    
354    }