001 //$HeadURL: svn+ssh://developername@svn.wald.intevation.org/deegree/base/trunk/src/org/deegree/io/datastore/memory/MemoryWFSDatastore.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
009 This library is free software; you can redistribute it and/or modify it under
010 the terms of the GNU Lesser General Public License as published by the Free
011 Software Foundation; either version 2.1 of the License, or (at your option)
012 any later version.
013 This library is distributed in the hope that it will be useful, but WITHOUT
014 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
015 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
016 details.
017 You should have received a copy of the GNU Lesser General Public License
018 along with this library; if not, write to the Free Software Foundation, Inc.,
019 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
020
021 Contact information:
022
023 lat/lon GmbH
024 Aennchenstr. 19, 53177 Bonn
025 Germany
026 http://lat-lon.de/
027
028 Department of Geography, University of Bonn
029 Prof. Dr. Klaus Greve
030 Postfach 1147, 53001 Bonn
031 Germany
032 http://www.geographie.uni-bonn.de/deegree/
033
034 e-mail: info@deegree.org
035 ----------------------------------------------------------------------------*/
036 package org.deegree.io.datastore.cached;
037
038 import java.io.InputStream;
039 import java.util.ArrayList;
040 import java.util.List;
041 import java.util.Properties;
042 import java.util.UUID;
043
044 import org.deegree.datatypes.QualifiedName;
045 import org.deegree.framework.log.ILogger;
046 import org.deegree.framework.log.LoggerFactory;
047 import org.deegree.framework.util.StringTools;
048 import org.deegree.i18n.Messages;
049 import org.deegree.io.datastore.Datastore;
050 import org.deegree.io.datastore.DatastoreException;
051 import org.deegree.io.datastore.DatastoreTransaction;
052 import org.deegree.io.datastore.schema.MappedFeatureType;
053 import org.deegree.io.datastore.schema.MappedGMLSchema;
054 import org.deegree.io.datastore.schema.MappedGMLSchemaDocument;
055 import org.deegree.io.rtree.HyperBoundingBox;
056 import org.deegree.io.rtree.HyperPoint;
057 import org.deegree.io.rtree.RTree;
058 import org.deegree.io.rtree.RTreeException;
059 import org.deegree.model.crs.CRSFactory;
060 import org.deegree.model.crs.CoordinateSystem;
061 import org.deegree.model.crs.UnknownCRSException;
062 import org.deegree.model.feature.Feature;
063 import org.deegree.model.feature.FeatureCollection;
064 import org.deegree.model.feature.FeatureFactory;
065 import org.deegree.model.feature.schema.FeatureType;
066 import org.deegree.model.filterencoding.ComplexFilter;
067 import org.deegree.model.filterencoding.Filter;
068 import org.deegree.model.filterencoding.FilterEvaluationException;
069 import org.deegree.model.filterencoding.FilterTools;
070 import org.deegree.model.spatialschema.Envelope;
071 import org.deegree.model.spatialschema.GeometryException;
072 import org.deegree.model.spatialschema.GeometryFactory;
073 import org.deegree.model.spatialschema.Position;
074 import org.deegree.ogcwebservices.wfs.operation.Query;
075
076 /**
077 * {@link Datastore} implementation that
078 *
079 * @author <a href="mailto:poth@lat-lon.de">Andreas Poth</a>
080 * @author last edited by: $Author: poth $
081 *
082 * @version $Revision: 6251 $, $Date: 2007-03-19 16:59:28 +0100 (Mo, 19 Mrz 2007) $
083 */
084 public class CachedWFSDatastore extends Datastore {
085
086 private ILogger LOG = LoggerFactory.getLogger( CachedWFSDatastore.class );
087
088 private static RTree rTree;
089
090 private static List<Feature> featureList;
091
092 private String srsName;
093
094 private int cacheSize = 100000;
095
096 /**
097 * default constructor; reads cache size from /cache.properties or if not available from
098 * cache.properties
099 *
100 */
101 public CachedWFSDatastore() {
102 try {
103 Properties prop = new Properties();
104 InputStream is = CachedWFSDatastore.class.getResourceAsStream( "/cache.properties" );
105 if ( is == null ) {
106 is = CachedWFSDatastore.class.getResourceAsStream( "cache.properties" );
107 }
108 prop.load( is );
109 is.close();
110 cacheSize = Integer.parseInt( prop.getProperty( "size" ) );
111 } catch ( Exception e ) {
112 cacheSize = 100000;
113 }
114 }
115
116 @Override
117 public void bindSchema( MappedGMLSchema schema )
118 throws DatastoreException {
119 super.bindSchema( schema );
120
121 srsName = schema.getDefaultSRS().toString();
122 try {
123 init();
124 } catch ( DatastoreException e ) {
125 e.printStackTrace();
126 }
127 }
128
129 private void init()
130 throws DatastoreException {
131 CachedWFSDatastoreConfiguration mconf = (CachedWFSDatastoreConfiguration) this.getConfiguration();
132 LOG.logInfo( "using cache size: " + cacheSize );
133 if ( mconf != null ) {
134 synchronized ( mconf ) {
135 QualifiedName ft = mconf.getFeatureType();
136
137 FeatureType fType = this.getSchemas()[0].getFeatureTypes()[0];
138 if ( rTree == null ) {
139 LOG.logInfo( "initializing MemoryWFSDatastore for faeturetype ", fType );
140 try {
141 rTree = new RTree( 2, cacheSize );
142 } catch ( RTreeException e ) {
143 LOG.logError( e.getMessage(), e );
144 throw new DatastoreException( e.getMessage(), e );
145 }
146 featureList = new ArrayList<Feature>( cacheSize );
147 int accessSize = 1000;
148 try {
149 Query query = Query.create( ft );
150 query.setMaxFeatures( accessSize );
151 MappedGMLSchemaDocument doc = new MappedGMLSchemaDocument();
152 doc.load( mconf.getSchemaLocation() );
153 MappedGMLSchema mgs = doc.parseMappedGMLSchema();
154 Datastore ds = mgs.getDatastore();
155
156 FeatureCollection fc = null;
157 int k = 1;
158 int c = 0;
159 do {
160 String s = StringTools.concat( 100, "reading feature: " , k , " - " , (k+accessSize) );
161 LOG.logInfo( s );
162 MappedFeatureType[] rootFts = new MappedFeatureType[] { mgs.getFeatureType( ft ) };
163 query.setStartPosition( k-1 );
164 fc = ds.performQuery( query, rootFts );
165 for ( int i = 0; i < fc.size(); i++ ) {
166 Feature feature = fc.getFeature( i );
167 // insert feature into RTree
168 featureList.add( feature );
169 insertIntoRTree( rTree, feature, c++ );
170 }
171 k += accessSize;
172 } while ( fc.size() == accessSize && k <= cacheSize );
173 LOG.logInfo( Integer.toString( featureList.size() ), " features loaded" );
174 } catch ( Exception e ) {
175 LOG.logError( e.getMessage(), e );
176 throw new DatastoreException( e.getMessage(), e );
177 }
178 }
179 }
180 }
181 }
182
183 private void insertIntoRTree( RTree rTree, Feature feature, int pos )
184 throws RTreeException {
185 Envelope envelope = null;
186 try {
187 envelope = feature.getBoundedBy();
188 } catch ( GeometryException e ) {
189 LOG.logError( e.getMessage(), e );
190 // maybe thrown because feature has no envelope; than use default BBOX
191 envelope = GeometryFactory.createEnvelope( -999999, -999999, -999998, -999998, null );
192 }
193
194 Position p = envelope.getMin();
195 HyperPoint min = new HyperPoint( new double[] { p.getX(), p.getY() } );
196 p = envelope.getMax();
197 HyperPoint max = new HyperPoint( new double[] { p.getX(), p.getY() } );
198 HyperBoundingBox hbb = new HyperBoundingBox( min, max );
199 rTree.insert( pos, hbb );
200
201 }
202
203 @Override
204 public CachedWFSAnnotationDocument getAnnotationParser() {
205 return new CachedWFSAnnotationDocument();
206 }
207
208 @Override
209 public void close()
210 throws DatastoreException {
211 // nothing to do
212 }
213
214 @Override
215 @SuppressWarnings("unused")
216 public FeatureCollection performQuery( Query query, MappedFeatureType[] rootFts, DatastoreTransaction context )
217 throws DatastoreException, UnknownCRSException {
218 return performQuery( query, rootFts );
219 }
220
221 @Override
222 @SuppressWarnings("unused")
223 public FeatureCollection performQuery( Query query, MappedFeatureType[] rootFts )
224 throws DatastoreException, UnknownCRSException {
225
226 if ( rootFts.length > 1 ) {
227 String msg = Messages.getMessage( "DATASTORE_SHAPE_DOES_NOT_SUPPORT_JOINS" );
228 throw new DatastoreException( msg );
229 }
230
231 MappedFeatureType ft = rootFts[0];
232
233 // perform CRS transformation (if necessary)
234 Query transformedQuery = transformQuery( query );
235
236 FeatureCollection result = null;
237 int startPosition = -1;
238 int maxFeatures = -1;
239
240 int record = -1;
241 try {
242 startPosition = transformedQuery.getStartPosition();
243 maxFeatures = transformedQuery.getMaxFeatures();
244 Filter filter = transformedQuery.getFilter();
245 Envelope bbox = null;
246 if ( filter instanceof ComplexFilter ) {
247 Object[] objects = null;
248 try {
249 objects = FilterTools.extractFirstBBOX( (ComplexFilter) filter );
250 } catch ( Exception e ) {
251 LOG.logError( e.getMessage(), e );
252 String msg = Messages.getMessage( "DATASTORE_EXTRACTBBOX", record );
253 throw new DatastoreException( msg, e );
254 }
255 bbox = (Envelope) objects[0];
256 filter = (Filter) objects[1];
257 }
258 if ( bbox == null ) {
259 bbox = GeometryFactory.createEnvelope( Integer.MIN_VALUE, Integer.MIN_VALUE, Integer.MAX_VALUE,
260 Integer.MAX_VALUE, null );
261 }
262
263 Position p = bbox.getMin();
264 HyperPoint min = new HyperPoint( new double[] { p.getX(), p.getY() } );
265 p = bbox.getMax();
266 HyperPoint max = new HyperPoint( new double[] { p.getX(), p.getY() } );
267 HyperBoundingBox hbb = new HyperBoundingBox( min, max );
268 Object[] obj = rTree.intersects( hbb );
269 // id=identity required
270 if ( obj != null ) {
271 // check parameters for sanity
272 if ( startPosition < 1 ) {
273 startPosition = 1;
274 }
275 if ( ( maxFeatures < 0 ) || ( maxFeatures >= obj.length ) ) {
276 maxFeatures = obj.length;
277 }
278 result = FeatureFactory.createFeatureCollection( UUID.randomUUID().toString(), obj.length );
279
280 // TODO: respect startposition
281
282 CoordinateSystem crs = CRSFactory.create( srsName );
283 for ( int i = 0; i < maxFeatures; i++ ) {
284 Feature feat = featureList.get( ( (Integer) obj[i] ).intValue() );
285 if ( filter == null || filter.evaluate( feat ) ) {
286 String msg = StringTools.concat( 200, "Adding feature '", feat.getId(),
287 "' to FeatureCollection (with CRS ", srsName, ")." );
288 LOG.logDebug( msg );
289
290 result.add( feat );
291 }
292 }
293
294 // update the envelopes
295 result.setEnvelopesUpdated();
296 result.getBoundedBy();
297 } else {
298 result = FeatureFactory.createFeatureCollection( UUID.randomUUID().toString(), 1 );
299 }
300 } catch ( FilterEvaluationException e ) {
301 throw new DatastoreException( e.getMessage(), e );
302 } catch ( Exception e ) {
303 LOG.logError( e.getMessage(), e );
304 String msg = Messages.getMessage( "DATASTORE_READINGFROMDBF", record );
305 throw new DatastoreException( msg, e );
306 }
307
308 // transform result to queried srs if necessary
309 String targetSrsName = transformedQuery.getSrsName();
310 if ( targetSrsName != null && !targetSrsName.equals( this.srsName ) ) {
311 result = transformResult( result, transformedQuery.getSrsName() );
312 }
313
314 return result;
315 }
316 }