001    //$HeadURL: svn+ssh://developername@svn.wald.intevation.org/deegree/base/trunk/src/org/deegree/io/datastore/memory/MemoryWFSDatastore.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.datastore.cached;
044    
045    import java.io.InputStream;
046    import java.util.ArrayList;
047    import java.util.List;
048    import java.util.Properties;
049    import java.util.UUID;
050    
051    import org.deegree.datatypes.QualifiedName;
052    import org.deegree.framework.log.ILogger;
053    import org.deegree.framework.log.LoggerFactory;
054    import org.deegree.framework.util.StringTools;
055    import org.deegree.i18n.Messages;
056    import org.deegree.io.datastore.Datastore;
057    import org.deegree.io.datastore.DatastoreException;
058    import org.deegree.io.datastore.DatastoreTransaction;
059    import org.deegree.io.datastore.schema.MappedFeatureType;
060    import org.deegree.io.datastore.schema.MappedGMLSchema;
061    import org.deegree.io.datastore.schema.MappedGMLSchemaDocument;
062    import org.deegree.io.rtree.HyperBoundingBox;
063    import org.deegree.io.rtree.HyperPoint;
064    import org.deegree.io.rtree.RTree;
065    import org.deegree.io.rtree.RTreeException;
066    import org.deegree.model.crs.CRSFactory;
067    import org.deegree.model.crs.CoordinateSystem;
068    import org.deegree.model.crs.UnknownCRSException;
069    import org.deegree.model.feature.Feature;
070    import org.deegree.model.feature.FeatureCollection;
071    import org.deegree.model.feature.FeatureFactory;
072    import org.deegree.model.feature.schema.FeatureType;
073    import org.deegree.model.filterencoding.ComplexFilter;
074    import org.deegree.model.filterencoding.Filter;
075    import org.deegree.model.filterencoding.FilterEvaluationException;
076    import org.deegree.model.filterencoding.FilterTools;
077    import org.deegree.model.spatialschema.Envelope;
078    import org.deegree.model.spatialschema.GeometryException;
079    import org.deegree.model.spatialschema.GeometryFactory;
080    import org.deegree.model.spatialschema.Position;
081    import org.deegree.ogcwebservices.wfs.operation.Query;
082    
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 CachedWFSDatastore extends Datastore {
093    
094        private ILogger LOG = LoggerFactory.getLogger( CachedWFSDatastore.class );
095    
096        private static RTree rTree;
097    
098        private static List<Feature> featureList;
099    
100        private String srsName;
101    
102        private int cacheSize = 100000;
103    
104        /**
105         * default constructor; reads cache size from /cache.properties or if not available from
106         * cache.properties
107         * 
108         */
109        public CachedWFSDatastore() {
110            try {
111                Properties prop = new Properties();
112                InputStream is = CachedWFSDatastore.class.getResourceAsStream( "/cache.properties" );
113                if ( is == null ) {
114                    is = CachedWFSDatastore.class.getResourceAsStream( "cache.properties" );
115                }
116                prop.load( is );
117                is.close();
118                cacheSize = Integer.parseInt( prop.getProperty( "size" ) );
119            } catch ( Exception e ) {
120                cacheSize = 100000;
121            }
122        }
123    
124        @Override
125        public void bindSchema( MappedGMLSchema schema )
126                                throws DatastoreException {
127            super.bindSchema( schema );
128    
129            srsName = schema.getDefaultSRS().toString();
130            try {
131                init();
132            } catch ( DatastoreException e ) {
133                e.printStackTrace();
134            }
135        }
136    
137        private void init()
138                                throws DatastoreException {
139            CachedWFSDatastoreConfiguration mconf = (CachedWFSDatastoreConfiguration) this.getConfiguration();
140            LOG.logInfo( "use cache size: " + cacheSize );
141            if ( mconf != null ) {
142                synchronized ( mconf ) {
143                    QualifiedName ft = mconf.getFeatureType();
144    
145                    FeatureType fType = this.getSchemas()[0].getFeatureTypes()[0];
146                    if ( rTree == null ) {
147                        LOG.logInfo( "initializing MemoryWFSDatastore for faeturetype ", fType );
148                        try {
149                            rTree = new RTree( 2, cacheSize );
150                        } catch ( RTreeException e ) {
151                            LOG.logError( e.getMessage(), e );
152                            throw new DatastoreException( e.getMessage(), e );
153                        }
154                        featureList = new ArrayList<Feature>( cacheSize );
155                        try {
156                            Query query = Query.create( ft );
157                            query.setMaxFeatures( 1000 );
158                            MappedGMLSchemaDocument doc = new MappedGMLSchemaDocument();
159                            doc.load( mconf.getSchemaLocation() );
160                            MappedGMLSchema mgs = doc.parseMappedGMLSchema();
161                            Datastore ds = mgs.getDatastore();
162    
163                            FeatureCollection fc = null;
164                            int k = 1;
165                            do {
166                                String s = StringTools.concat( 100, "reading feature: " , k , " - " , (k+1000) );
167                                LOG.logInfo( s );  
168                                MappedFeatureType[] rootFts = new MappedFeatureType[] { mgs.getFeatureType( ft ) };
169                                query.setStartPosition( k-1 );                            
170                                fc = ds.performQuery( query, rootFts );
171                                for ( int i = 0; i < fc.size(); i++ ) {
172                                    Feature feature = fc.getFeature( i );
173                                    // insert feature into RTree
174                                    featureList.add( feature );
175                                    insertIntoRTree( rTree, feature, i );
176                                }
177                                k += 1000;
178                            } while ( fc.size() == 1000 );
179                            LOG.logInfo( Integer.toString( fc.size() ), " features loaded" );
180                        } catch ( Exception e ) {
181                            LOG.logError( e.getMessage(), e );
182                            throw new DatastoreException( e.getMessage(), e );
183                        }
184                    }
185                }
186            }
187        }
188    
189        private void insertIntoRTree( RTree rTree, Feature feature, int pos )
190                                throws RTreeException {
191            Envelope envelope = null;
192            try {
193                envelope = feature.getBoundedBy();
194            } catch ( GeometryException e ) {
195                LOG.logError( e.getMessage(), e );
196                // maybe thrown because feature has no envelope; than use default BBOX
197                envelope = GeometryFactory.createEnvelope( -999999, -999999, -999998, -999998, null );
198            }
199    
200            Position p = envelope.getMin();
201            HyperPoint min = new HyperPoint( new double[] { p.getX(), p.getY() } );
202            p = envelope.getMax();
203            HyperPoint max = new HyperPoint( new double[] { p.getX(), p.getY() } );
204            HyperBoundingBox hbb = new HyperBoundingBox( min, max );
205            rTree.insert( pos, hbb );
206    
207        }
208    
209        @Override
210        public CachedWFSAnnotationDocument getAnnotationParser() {
211            return new CachedWFSAnnotationDocument();
212        }
213    
214        @Override
215        public void close()
216                                throws DatastoreException {
217        }
218    
219        @Override
220        @SuppressWarnings("unused")
221        public FeatureCollection performQuery( Query query, MappedFeatureType[] rootFts, DatastoreTransaction context )
222                                throws DatastoreException, UnknownCRSException {
223            return performQuery( query, rootFts );
224        }
225    
226        @Override
227        @SuppressWarnings("unused")
228        public FeatureCollection performQuery( Query query, MappedFeatureType[] rootFts )
229                                throws DatastoreException, UnknownCRSException {
230    
231            if ( rootFts.length > 1 ) {
232                String msg = Messages.getMessage( "DATASTORE_SHAPE_DOES_NOT_SUPPORT_JOINS" );
233                throw new DatastoreException( msg );
234            }
235    
236            MappedFeatureType ft = rootFts[0];
237    
238            // perform CRS transformation (if necessary)
239            query = transformQuery( query );
240    
241            FeatureCollection result = null;
242            int startPosition = -1;
243            int maxFeatures = -1;
244    
245            int record = -1;
246            try {
247                startPosition = query.getStartPosition();
248                maxFeatures = query.getMaxFeatures();
249                Filter filter = query.getFilter();
250                Envelope bbox = null;
251                if ( filter instanceof ComplexFilter ) {
252                    Object[] objects = null;
253                    try {
254                        objects = FilterTools.extractFirstBBOX( (ComplexFilter) filter );
255                    } catch ( Exception e ) {
256                        LOG.logError( e.getMessage(), e );
257                        String msg = Messages.getMessage( "DATASTORE_EXTRACTBBOX", record );
258                        throw new DatastoreException( msg, e );
259                    }
260                    bbox = (Envelope) objects[0];
261                    filter = (Filter) objects[1];
262                }
263                if ( bbox == null ) {
264                    bbox = GeometryFactory.createEnvelope( Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE,
265                                                           Integer.MAX_VALUE, null );
266                }
267    
268                Position p = bbox.getMin();
269                HyperPoint min = new HyperPoint( new double[] { p.getX(), p.getY() } );
270                p = bbox.getMax();
271                HyperPoint max = new HyperPoint( new double[] { p.getX(), p.getY() } );
272                HyperBoundingBox hbb = new HyperBoundingBox( min, max );
273                Object[] obj = rTree.intersects( hbb );
274                // id=identity required
275                if ( obj != null ) {
276                    // check parameters for sanity
277                    if ( startPosition < 1 ) {
278                        startPosition = 1;
279                    }
280                    if ( ( maxFeatures < 0 ) || ( maxFeatures >= obj.length ) ) {
281                        maxFeatures = obj.length;
282                    }
283                    result = FeatureFactory.createFeatureCollection( UUID.randomUUID().toString(), obj.length );
284    
285                    // TODO: respect startposition
286    
287                    CoordinateSystem crs = CRSFactory.create( srsName );
288                    for ( int i = 0; i < maxFeatures; i++ ) {
289                        Feature feat = featureList.get( ( (Integer) obj[i] ).intValue() );
290                        if ( filter == null || filter.evaluate( feat ) ) {
291                            String msg = StringTools.concat( 200, "Adding feature '", feat.getId(),
292                                                             "' to FeatureCollection (with CRS ", srsName, ")." );
293                            LOG.logDebug( msg );
294    
295                            result.add( feat );
296                        }
297                    }
298    
299                    // update the envelopes
300                    result.setEnvelopesUpdated();
301                    result.getBoundedBy();
302                } else {
303                    result = FeatureFactory.createFeatureCollection( UUID.randomUUID().toString(), 1 );
304                }
305            } catch ( FilterEvaluationException e ) {
306                throw new DatastoreException( e.getMessage(), e );
307            } catch ( Exception e ) {
308                LOG.logError( e.getMessage(), e );
309                String msg = Messages.getMessage( "DATASTORE_READINGFROMDBF", record );
310                throw new DatastoreException( msg, e );
311            }
312    
313            // transform result to queried srs if necessary
314            String targetSrsName = query.getSrsName();
315            if ( targetSrsName != null && !targetSrsName.equals( this.srsName ) ) {
316                result = transformResult( result, query.getSrsName() );
317            }
318    
319            return result;
320        }
321    
322    }