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 }